-
Notifications
You must be signed in to change notification settings - Fork 15
SSR compatibility #21
Comments
To be honest, I've never done SSR myself and don't know what is involved, what the pitfalls are and what might make dynostore incompatible. Essentially, all dynostore does is add a redux enhancer and accesses the store in React context to call some functions when the component did mount, so I think if all those pieces are SSR compatible, it should just work. If you want to set up an example (even submit it as a PR?) and it doesn't work, I'll gladly take a look. |
(i know Michael already answered, but I write this out on my phone in bed so I didn't want to waste it). SSR is not something we use (yet) so it has not been tested or a focus of this library. I can't see why the core packages wouldn't work (in theory) although I do know there are tricky bits about SSR that could trip us up. The more specific package for sagas (or other compatibility enhancers/plugins that's others may have written) would be dependent on the other library supporting it. I won't get time today, but I'll see if I can get an example working soon (or you can submit one via a PR and we can take a look) |
Also, just to clarify, dynostore does not do any code splitting itself. Usually you would wrap the top level component inside the split chunk in |
We have an example of using react-loadable in our micro-frontends example. I don't really want to dilute this question with that issue, so if you have questions about code splitting and dynostore, let's do it in another issue. |
And the real world example. |
@jpeyper @mpeyper thx for your fast answers, now I can see that code-splitting won't cause any problems for SSR. There are different problems though. I checked your documentation and I suspect that SSR won't be possible for now. To be able to do SSR, we need 2 things:
I am not sure about this one, in pure redux you can pass it like {
static: {} // created with a static reducer
dynamic: {} // created with a dynamic reducer, during component mount
} Assuming that dynamic reducer was generated on server side, would dynamic reducer on client side pick
Usually this is achieved by adding static methods to top level components, which for instance dispatch redux thunks which return promise, which u wrap in Promise.all after which your store will be populated. For Redux-Saga, there are different possibilities, for instance https://github.com/redux-saga/redux-saga/blob/master/examples/real-world/store/configureStore.dev.js#L32 and https://github.com/redux-saga/redux-saga/blob/master/examples/real-world/server.js#L59, so your sagas can do required job before u start rendering, again to have the store populared. I guess this is not possible to do, right? We would need a mechanism to await something before rendering component passed to |
This is where my knowledge of SSR is falling short as I don't think I know enough to actually help here...
In theory, this should work. The dynamic section of the state should get ignored by the static reducer and passed to the dynamic reducer when The bit I'm unsure of is if the dynamic component will get a chance to attach again once it hits the client. I'm assuming the idea with Redux in SSR is that you just reconstruct the store with the reducer and the rehydrated state, but the dynamic reducer is not known when the store is constructed, so how do we transfer that knowledge from the server to the client?
The standard way to use redux-dynostore is to attach the reducer when the React component is needed. This obviously causes a problem for the above use case. Part of the benefit is that we don't attach reducers if they are not needed due to conditional rendering. That said, React is an optional part of redux-dynostore, so if you can identify a reducer will be required through some other mechanism (e.g. the result of a previous AJAX request), then you can call the const store = createStore(staticReducers, dynostore(dynamicReducers()))
const promise1 = store.dispatch(makeRequest1())
const promise2 = promise1.then(data => {
if (data.someCondition) {
store.attachReducers({ dynamicReducer })
return store.dispatch(makeRequest2())
}
return Promise.resolve()
})
Promise.all([promise1, promise2]).then(() => {
// render app
}) You could still use a Does any of this help? |
@mpeyper yeah, thx for the detailed answer.
Using this library state is still global, so I imagine to have a method on the server which would merge static state and dynamic state from dynamic reducers into one object. Then, on the client side, we could pass it in Regarding 2nd problem, either we would need to have a configuration per route which dynamic slices are needed for a given route, or... to have a mechanism like in apollo to walk through all components to know which In the future it should be much easier because future React will allow to stop rendering, do sth, like run sagas, then continue rendering. So I think that it is better to wait for this new React to come, then solving this problem will be much easier and SSR will be much more performant (comparing to apollo method) |
Just FYI, I came across this discussion about using replaceReducers (how dynostore works) when SSR and some issues that it has in the react-redux v6 that we might need to watch out for. |
@jpeyper interesting, let's hope they will solve it in react-redux, they have several ideas already. This is about reducers only though, dynamic sagas/epics which fetch data with requests are a different story, and I feel that with new React fetcher API we will be able to do SSR with only one render and without any extra configuration on the app level. I am going to really spend some time on this issue in the next year, when new React will be there, for now I guess I will just code split only React components, I shouldn't have any issues fetching whole redux for all routes for now. But I think that splittable Redux compatible with SSR would be really so much beneficial, allowing scaling state layer of complex apps with maintaining single store concept. If you prefer, you could close this issue for now, or just keep it opened for some time until circumstances of developing this feature will be better, I don't mind either way. |
Any updates on this issue? |
Not from us, we're still not using SSR and don't plan to any time soon but we do have plans to support redux v6 soon (#20) Not sure if @klis87 has tried out anything more now redux v6 is out now, or progressed further with SSR. As always, if you're able and need the feature, we would love help solving this. PRs welcome. |
@jpeyper I didn't, because I really think that we need to wait for possibility to pause rendering on the server and resume, for example after a promise is resolved. Now it is not possible, that's why in many cases SSR involves double rendering (even 1st sometimes is not a full render like in react-apollo, but still) For now I opted for an alternative solution to redux code splitting.Instead of code splitting, I created a library for managing remote data and remote data usually takes 90% of state. So because remote state is managed by one reducer, code splitting isn't needed so much anymore. |
Closing. Please see #484 for details. |
Is it a bug, feature request or question?
Question/Feature request
Which package(s) does this involve?
Probably every package, but mostly react-redux binding
Expected Behavior
Does this library support server side rendering? I know SSR + code splitting is quite a tricky combination, but thx for libraries like react-loadable or loadable-components it is possible now for React. I am wondering whether this is possible together with
dynostore
and if not, whether it would be difficult to achieve.The text was updated successfully, but these errors were encountered: