How to use a reducer #259
Replies: 1 comment 2 replies
-
|
Hey @erictompkins yeah the reducer documentation could use some atoms-specific examples. Using stores in components
Your example works (though const placedBlocksInstance = useAtomInstance(atomWithReducer)
const placedBlocks = useAtomValue(placedBlocksInstance) // make this component rerender on atom instance state change
// then use `placedBlocksInstance.dispatch` to dispatch actionsState from props
Good question. There are few ways to do it. Passing the prop directly, like in your example, would work if you're only accessing the atom from one component and want to create a completely new atom instance when the forwarded props change. The idea in general only makes sense if you know for sure that the atom will be instantiated from the props-passing component. And, as with all state created from props, you have to decide what should happen when props change. Usually a setup like that means the parent that's passing the props should own the state and a rework is in order. But one valid scenario I've seen many times is where you create the initial state using a "snapshot" of the initial props, and the props never influence the state after that. I'll assume that scenario here. With that in mind, I'd probably use a ref in another atom like this: const propsRefAtom = atom('refs', () => {
const propsRef = injectRef()
return api().setExports({ propsRef })
})
const reducerStoreAtom = atom('reducerStore', () => {
const { propsRef } = injectAtomInstance(propsRefAtom).exports
const store = injectStore(() => createStore(myReducer, propsRef.current))
return store
})
function ChildComponent({ someProp }) {
useAtomInstance(propsRefAtom).exports.propsRef.current = someProp
// now, assuming this is the first time `reducerStoreAtom` is used, the initial state will be someProp
const reducerStoreInstance = useAtomInstance(reducerStoreAtom)
}Typed dispatches
Zedux's normal stores and That said, there is another way to accomplish this. And it's actually what we do in a few places: Extend the store class. class CustomStore<State, Actions extends Record<string, any>> extends Store<State> {
typedDispatch = <Type extends keyof Actions & string>(action: {
type: Type;
payload: Actions[Type];
}) => this.dispatch(action);
}
// then instantiate and use anywhere you'd use a store (it is a full-fledged Zedux store after all)
type Actions = {
add: number;
};
const store = new CustomStore<number, Actions>(myReducer, 1)
store.typedDispatch({ type: 'invalidType', payload: 3 }) // error
store.typedDispatch({ type: 'add', payload: 'invalid payload' }) // error
store.typedDispatch({ type: 'add', payload: 3 }) // good
// note that `injectStore` doesn't have type support for custom store classes.
// Either use a type cast or use `injectMemo` + `injectEffect` to subscribe
const customStoreAtom = atom('customStore', () => {
const customStore = injectStore(() => new CustomStore(myReducer, 1)) as CustomStore<
number,
Actions
>;
return customStore;
});
function ExampleComponent() {
// atoms fully support custom stores. TS knows this instance has `.store.typedDispatch`
const customStoreInstance = useAtomInstance(customStoreAtom);
const state = useAtomValue(customStoreInstance);
return (
<div>
<div>state: {state}</div>
<button onClick={() => customStoreInstance.store.typedDispatch({ type: 'add', payload: 2 })}>
Add 2
</button>
</div>
);
}Last stuffWhile using custom stores is pretty easy, using them to solve this problem is admittedly less perfect than if Zedux supported these types out of the box. While Zedux v2 moves away from using stores at its core, stores are still supported as an addon via the Since they're not abandoned, I'd be willing to spend some time adding this to the stores package and backporting that to v1, if this is a much needed feature. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I'm trying to set up a reducer but I'm having trouble using it.
I'm assuming that in order for a component to use the reducer state I need to include the reducer in an atom like this in basic pseudo code:
However, I'm not sure how to use that in our components so that I can access the current state of the atom, and so that I can use it in my components.
I'm guessing this, but I'm not sure.
Two other questions.
What is the best way to pass a default value to the reducer if the value is coming from a prop in a component?
Something like this:
Lastly, what is the recommended way to set up types with TypeScript so that we get type hints when using the
dispatchfunction?Thank you.
Beta Was this translation helpful? Give feedback.
All reactions