redux-ui
redux-ui copied to clipboard
Does not work with server-side rendering because of Immutable
After the state gets serialized into plain JavaScript objects on the server-side, and deserialized on the client-side, the action-reducer gets a non-default state of type Object, not Immutable Map it expects.
const defaultState = new Map({
// ...
});
export default function reducer(state = defaultState, action) {
// `state` is of type `Object` on state rehydration from the serialized data.
So, this line fails with an exception about missing get
function:
https://github.com/tonyhb/redux-ui/blob/master/src/action-reducer.js#L96
const customReducers = state.get('__reducers');
You use Immutable
objects in actions, too. This can fail if these actions get serialized and replayed later (one of the core features of Redux is replayability, mainly for testing and debugging purposes).
https://github.com/tonyhb/redux-ui/blob/master/src/action-reducer.js#L138
https://github.com/tonyhb/redux-ui/blob/master/src/action-reducer.js#L165
Having a non-serializable (non-POJO) state and actions leads to such incompatible behavior.
Please either drop Immutable
entirely -- it really appears to be unnecessary -- or make the state and actions serializable by converting Immutable
objects to POJO via .toJs()
.
Christmas and new years happened, apologies for the delay. Getting around to this little runt between now and Wednesday.
Been thinking about this over the past few days. I don't want to use immutable because I feel as if Immutable should always be used in reducers. It helps us:
- Strictly compare state for optimization, if desired
- Deeply set, get and merge properties within a tree
We do server-side rendering at Docker and I'd expect that if you're casting .toJS() on the store as you deflate the state you should also inflate the state using .fromJS
during your setState() call — that's how we're handling this.
I'll see if we can make an inflate()
method ourselves that recurses down the UI state tree converting each component's state to an object. That's a potential plan.
I agree that Immutable within an action is incorrect; I'll change that in an upcoming PR.
Have you looked at freeze-during-development middleware, and thought of not using Immutable
at all for most of the cases? If, for some action, you need a deep manipulation on the state in the reducer, you could wrap the current state into an Immutable
via fromJS
, do the mutations, then convert the new state object back to POJO via toJS
and return it.
I'm sure redux-ui
should not impose using Immutable.
Deeply getting/setting and strict comparison are two big perks of Immutable. I don't see why it's such a dealbreaker.
@tonyhb Because it's not serializable without calling any methods, AFAIK.
Working on this issue at the moment too, going to try @tonyhb first suggestion.
I am doing this
initialData.ui = Immutable.fromJS(initialData.ui);
let store = createStore(reducer, initialData);
And get the same error, tried a few other variations, not quite sure what to do. =D
Edit: Ignore this, the above works.
Seems like you could just auto detect this pretty easy? Or throw a warning "It looks like you are trying to pass a PJO to the reducer, please lock it and try again."
@thomasdavis your solution does not work for me. The problem AFAIK is that is still returns a TypeError on the following line. Because in my initialState there is obviously no '__reducer' key, and so customReducers is undefiend https://github.com/tonyhb/redux-ui/blob/master/src/action-reducer.js#L98
@tonyhb how can i have a initialUIState without having a '__reducer' field in it? Maybe as a soluttion a notSetValue on the get function.
const customReducers = state.get('__reducers', new Map());
i would really love to see immutable been removed from this module. the mutations are not that hard to do without depending on immutable that is over 56kb minified. So if i use this module in my project not depending on immutablejs it will add me 56kb for 'nothing'.
If mutation gets to complex one could still use http://facebook.github.io/react/docs/update.html for this cases.
But sure for projects already using immutable it's ok.
I hear you and will look at removing immutable from the project for 1.0
For us Immutable.js with the 56KB is a deal breaker for using this library. Would love to see it removed and this library being much lighter with dependencies
Thanks for the heads up. Seems to be a fairly common request so we'll take a look and maybe have two reducers you can import; one for immutable and one for objects (potentially using lodash for _.set and _.get methods)
Hi, any progress on removing the immutable lib?
@tonyhb anything we can do to help out? I really like the API this provides over https://github.com/threepointone/redux-react-local, but I'm restricted at work and can't really use ImmutableJS.
For anyone interested, I have tried to make it work without ImmutableJS and I haven't encountered any issues (yet). You can use that fork until it's here.
For anyone interested an alternative solution without any dependencies on immutableJS is https://github.com/gcazaciuc/redux-fractal
Any news about it? The repo is still active? Last commit is in May
Wish I stumbled upon this sooner. I'm using https://github.com/tonyhb/tectonic and was really liking that module. Without redux-ui, I still would have to carry some of the actions/reducers to update the component. This muddies the water. Shame this repo has fallen into disrepair.
At least hand this over to OSS community so that it can be kept up-to-date.
Correction: Looks like the author is still active, just not paying attention to this issue anymore. ANy specific reason @tonyhb for this neglect? Especially when @rcsole seems to have a PR ready that'll fix this?
Figured out a workaround. In addition to "immutable-ising" ui
, one needs to do the same for tectonic
(if using tectonic)
initialState.ui = Immutable.fromJS(initialState.ui)
initialState.tectonic = Immutable.fromJS(initialState.tectonic)
Works for now. HMR is broken as mentioned in #21 so will have to wait for the 1.0 package. :(