-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Usage with immer #45
Comments
Hi @nmklotas
Yes, there are some workarounds to make internal integration of Deox with immer but I'm not sure about them for now.
A simple example of using immer with Deox could be like below: import produce from 'immer'
const todos = createReducer(defaultState, handleAction => [
handleAction(addTodo, (state, { payload }) =>
produce(state, draft => {
draft.push(payload)
})
),
]) For a reducer factory that uses immer you can do as follows for now: import produce from 'immer'
function createImmerReducer(defaultState, handlerMapsCreator) {
const handlerMap = merge(...handlerMapsCreator(createHandlerMap))
return (state, action) => {
const handler = handlerMap[action.type]
return handler ? produce(state, draft => handler(draft, action)) : state
}
} |
Thanks for the response! To createImmerReducer I would need additional exports from your package, otherwise TypeScript will would not find them: can you add these exports?
|
Some of those API may change without deprecations or breaking changes. So you can try submodule imports at your own risk in the following way: import { HandlerMap, CreateHandlerMap, createHandlerMap } from 'deox/create-handler-map'
import { merge } from 'deox/utils' |
I will close the issue for housekeeping purposes. Feel free to re-open it if needed. |
I have an idea but I need more community feedback about it. The idea is that export an API from Deox for extensions which can add more functionality to For example in immer it could be like below: import { createReducer } from 'deox'
const userReducer = createReducer(defaultState, handleAction => [
handleAction.immer(updateUser, (draft, { payload }) => {
state.username = payload.username || draft.username
state.isAdmin = payload.isAdmin || draft.isAdmin
})
]) Personally, I don't use immer. So @nmklotas @Haaxor1689 @LouizFC @rlesniak if you guys have experience on it, it would be great to help us here. |
Looks good, but I think the naming should be Also, in immer you can't replace the entire state object, like produce(state, (draft) => {
draft = newState;
}) So it should be taken into account |
Which aspect of this pitfall should we consider in Deox? |
I don't think it is something that should be addressed in code. Just documenting the usage patterns should be enough. Replacing the entire state tree is rare, and happens mostly on "sub-reducers". If someone need to replace the entire state, then instead of using Something like: import { createReducer } from 'deox'
const userReducer = createReducer(defaultState, handleAction => [
handleAction.immer(updateUser, (draft, { payload }) => {
draft.username = payload.username || state.username
draft.isAdmin = payload.isAdmin || state.isAdmin
}),
handleAction(replaceUserData, (state, { payload } => payload)
]) Or a similar pattern Edit: Also, in immer you can't create a "Draft of a Draft" (at least you couldn't, I will try to test it later to see if it stays true), so having the original state object is good. The handler could look something like: handleAction.immer(updateUser, (state, draft, { payload }) => {
draft.username = payload.username || state.username
draft.isAdmin = payload.isAdmin || state.isAdmin
}) Edit 2: another option is using the mechanism that immer uses for this cases, read it here and here |
Is it a pattern in immer to create a draft from a draft?
Can you explain more about the benefit(s) or use case(s) in favor of this extra argument? |
Everything that I will explain can probably be found and better explained on Immer README, but I will try to sum up some things
Immer drafts uses Proxy behind the scenes on compatible browsers, and fallsback to a "polyfilled" version on non-compatible browsers. That means that these These perfomance costs are not exorbitant, so it is not a "do or die" situation. It is recommended to use the original state for accessing properties instead of the Also, it is recommended not to "leak" the draft object into the application, because it will throw an error if you try to do anything with it after Making functions to make reusable "mutations" is ok, but using draft as if it was the original object is bug prone.
No, I just mentioned it because I had a case in mind but forgot to share more context. Sometimes you need to call a reducer inside another reducer, like so: handle(resolveQuery, (state, action) =>
produce(state, (draft) => {
draft.destination = findNearestDestination(state, action.payload);
draft.query = QueryReducer(state.query, action);
})
), I can't tell if passing a draft to |
As this issue should handle by plugins, I'm gonna close it until plugins system become into action. Feel free to reopen the issue if it's needed. |
Hello,
Wonderful library!
Do you have plans to integrate immer (https://github.com/mweststrate/immer)?
That would make writing reducers very easy
I'm trying to create my own version of createReducer that uses immer but without luck, maybe you know how to use your library with immer?
Thanks
The text was updated successfully, but these errors were encountered: