https://github.com/evgenyifedotov/effector-reflect
☄️ Render react components by effector stores.
https://github.com/evgenyifedotov/effector-reflect
effector react
Last synced: about 1 year ago
JSON representation
☄️ Render react components by effector stores.
- Host: GitHub
- URL: https://github.com/evgenyifedotov/effector-reflect
- Owner: EvgenyiFedotov
- Created: 2020-11-21T17:40:05.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2020-12-21T19:29:00.000Z (over 5 years ago)
- Last Synced: 2025-04-06T06:08:45.445Z (about 1 year ago)
- Topics: effector, react
- Language: TypeScript
- Homepage:
- Size: 388 KB
- Stars: 12
- Watchers: 2
- Forks: 1
- Open Issues: 4
-
Metadata Files:
- Readme: Readme.md
Awesome Lists containing this project
README
# Effector-reflect
☄️ Render react-components by effector stores.
## Install
### Npm
```sh
npm install effector-reflect
```
### Yarn
```sh
yarn add effector-reflect
```
## Motivation
### Common ui
```tsx
// ./ui.ts
import React, { FC, ChangeEvent, useCallback } from 'react';
type InputProps = {
value: string;
onChange: ChangeEvent;
};
export const Input: FC = ({ value, onChange }) => {
return ;
};
```
### Old case
```tsx
// ./old-case.ts
import React, { FC, ChangeEvent, useCallback } from 'react';
import { createEvent, restore } from 'effector';
import { useStore } from 'effector-react';
import { Input } from './ui';
// Model
const changeName = createEvent();
const $name = restore(changeName, '');
// Component
export const Name: FC = () => {
const value = useStore($name);
const changed = useCallback(
(event: ChangeEvent) => changeName(event.target.value),
[],
);
return ;
};
```
### New case
```tsx
// ./new-case.ts
import { createEvent, restore } from 'effector';
import { reflect } from 'effector-reflect';
import { Input } from './ui';
// Model
const changeName = createEvent();
const $name = restore(changeName, '');
// Component
export const Name = reflect({
view: Input,
bind: { value: $name, onChange: (event) => changeName(event.target.value) },
});
```
## Reflect
Method for bind stores to a view.
```tsx
// ./user.tsx
import React, { FC, useCallback, ChangeEvent } from 'react';
import { createEvent, restore } from 'effector';
import { reflect } from 'effector-reflect';
// Base components
type InputProps = {
value: string;
onChange: ChangeEvent;
placeholder?: string;
};
const Input: FC = ({ value, onChange, placeholder }) => {
return ;
};
// Model
const changeName = createEvent();
const $name = restore(changeName, '');
const changeAge = createEvent();
const $age = restore(changeAge, 0);
const inputChanged = (event: ChangeEvent) => {
return event.currentTarget.value;
};
// Components
const Name = reflect({
view: Input,
bind: {
value: $name,
onChange: changeName.prepend(inputChanged),
},
});
const Age = reflect({
view: Input,
bind: {
value: $age,
onChange: changeAge.prepend(parseInt).prepend(inputChanged),
},
});
export const User: FC = () => {
return (
);
};
```
## Create reflect
Method for creating reflect a view. So you can create a UI kit by views and use a view with a store already.
```tsx
// ./ui.tsx
import React, { FC, useCallback, ChangeEvent, MouseEvent } from 'react';
import { createReflect } from 'effector-reflect';
// Input
type InputProps = {
value: string;
onChange: ChangeEvent;
};
const Input: FC = ({ value, onChange }) => {
return ;
};
export const reflectInput = createReflect(Input);
// Button
type ButtonProps = {
onClick: MouseEvent;
title?: string;
};
const Button: FC = ({ onClick, children, title }) => {
return (
{children}
);
};
export const reflectButton = createReflect(Button);
```
```tsx
// ./user.tsx
import React, { FC } from 'react';
import { createEvent, restore } from 'effector';
import { reflectInput, reflectButton } from './ui';
// Model
const changeName = createEvent();
const $name = restore(changeName, '');
const changeAge = createEvent();
const $age = restore(changeAge, 0);
const submit = createEvent();
// Components
const Name = reflectInput({
value: $name,
onChange: (event) => changeName(event.target.value),
});
const Age = reflectInput({
value: $age,
onChange: (event) => changeAge(parsetInt(event.target.value)),
});
const Submit = reflectButton({
onClick: () => submit(),
});
export const User: FC = () => {
return (
Save left
Save right
);
};
```
## SSR
For SSR need to replace imports `effector-reflect` -> `effector-reflect/ssr`.
Also use `event.prepend(params => params)` instead `(params) => event(params)`.
```tsx
// ./ui.tsx
import React, { FC, useCallback, ChangeEvent, MouseEvent } from 'react';
// Input
type InputProps = {
value: string;
onChange: ChangeEvent;
};
const Input: FC = ({ value, onChange }) => {
return ;
};
```
```tsx
// ./app.tsx
import React, { FC } from 'react';
import { createEvent, restore, Fork, createDomain } from 'effector';
import { reflect } from 'effector-reflect/ssr';
import { Provider } from 'effector-react/ssr';
import { Input } from './ui';
// Model
export const app = createDomain();
export const changeName = app.createEvent();
const $name = restore(changeName, '');
// Component
const Name = reflect({
view: Input,
bind: { value: $name, onChange: changeName.prepend(event => event.target.value) },
});
export const App: FC<{ data: Fork }> = ({ data }) => {
return (
);
};
```
```tsx
// ./server.ts
import { fork, serialize, allSettled } from 'effector/fork';
import { App, app, changeName } from './app';
const render = async () => {
const scope = fork(app);
await allSettled(changeName, { scope, params: 'Bob' });
const data = serialize(scope);
const content = renderToString();
return `
${content}
window.__initialState__ = ${JSON.stringify(data)};
`;
};
```
## Roadmap
- [] Auto moving test from ./src to ./dist-test