Сделать независимые копии стора для инстансов компонента
Как правильно сделать, чтобы эти Counter'ы работали независимо, а не увеличивались вместе, как сейчас?
https://share.effector.dev/YuZvKpqn
import React from 'react'
import ReactDOM from 'react-dom'
import { createEvent, createStore } from 'effector'
import { useUnit } from 'effector-react'
const inc = createEvent()
const $counter = createStore(0).on(inc, n => n + 1)
function Counter() {
const [counter, incFx] = useUnit([$counter, inc])
return <button onClick={incFx}>{counter}</button>
}
function App() {
return (
<div>
<Counter />
{" "}
<Counter />
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Насколько я понимаю, надо копать в сторону fork и Provider, и мне даже удалось сделать так:
https://share.effector.dev/08HXMbOo
import React from 'react'
import ReactDOM from 'react-dom'
import { createEvent, createStore, fork } from 'effector'
import { useUnit, Provider } from 'effector-react'
const inc = createEvent()
const $counter = createStore(0).on(inc, n => n + 1)
function Counter() {
const [counter, incFx] = useUnit([$counter, inc])
return <button onClick={incFx}>{counter}</button>
}
const scope1 = fork()
const scope2 = fork()
function App() {
return (
<div>
<Provider value={scope1}>
<Counter />
</Provider>
{" "}
<Provider value={scope2}>
<Counter />
</Provider>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Но у меня ощущение, что это решение неправильное и на более сложном коде вызовет кучу проблем, например:
- я не могу так создать произвольное количество Counter'ов - например, на основе массива
- fork форкает все сторы, а не только
$counter- получается, если я захочу внутри провайдера изменить какой-то общий стор, то у меня это не выйдет, поскольку он изменится только в рамках скоупа
Как сделать так, чтобы всё правильно работало в чём-то таком?
https://share.effector.dev/VVz0vtnu
import React from 'react'
import ReactDOM from 'react-dom'
import { createEvent, createStore } from 'effector'
import { useUnit } from 'effector-react'
const inc = createEvent()
const $counter = createStore(0).on(inc, n => n + 1)
const incCommon = createEvent()
const $common = createStore(0).on(incCommon, n => n + 1)
function Counter({ name }) {
const [counter, incFx, common, incCommonFx] = useUnit([$counter, inc, $common, incCommon])
return (
<p>
<button onClick={incFx}>{name}: {counter}</button>
{" "}
<button onClick={incCommonFx}>Common: {common}</button>
</p>
)
}
function App({ names }) {
return (
<div>
{names.map(name => <Counter name={name} />)}
</div>
)
}
ReactDOM.render(<App names={["Jack", "Peter"]} />, document.getElementById('root'))
Ответы (1 шт):
Проблема в том, что в вашем примере createStore и createEvent создают глобальные синглтоны вне компоненты. Когда вы импортируете $counter и inc, вы всегда получаете одни и те же экземпляры.
Через fork в теории можно, вы создаёте общий объект, но затем просите сделать копию (которую можно было бы форкнуть внутри компоненты кстати, но не суть) и потом её используете.
На мой взгляд правильнее было бы создавать счётчик через подобие фабрики. https://share.effector.dev/cXfKMHC2
import React from 'react'
import ReactDOM from 'react-dom'
import { createEvent, createStore } from 'effector'
import { useUnit } from 'effector-react'
// Фабрика для создания изолированных сторов и событий
const createCounter = () => {
const inc = createEvent()
const $counter = createStore(0).on(inc, n => n + 1)
return { inc, $counter }
}
// Общий стор и событие, ?если нужно?
const incCommon = createEvent()
const $common = createStore(0).on(incCommon, n => n + 1)
function Counter({ name }) {
// Создаём изолированные стор и события внутри конкретной компоненты
const { $counter, inc } = React.useMemo(createCounter, [])
const [counter, incFx] = useUnit([$counter, inc])
const [common, incCommonFx] = useUnit([$common, incCommon])
return (
<p>
<button onClick={incFx}>{name}: {counter}</button>
{" "}
<button onClick={incCommonFx}>Common: {common}</button>
</p>
)
}
function App({ names }) {
return (
<div>
{names.map(name => <Counter key={name} name={name} />)}
</div>
)
}
ReactDOM.render(<App names={["Jack", "Peter"]} />, document.getElementById('root'))