react-tree-walker
react-tree-walker copied to clipboard
fixing React.createContext in SSR
Hi, would you consider making changes analogous to https://github.com/apollographql/react-apollo/pull/2304 in order to fix context handling? I'm happy to create pull request for that if you'd like to. Thanks!
As another reference, I've recently fixed the graphql-react
SSR preload
function for React v16.6 context: Diff here.
@tadeuszwojcik of course, always happy to keep inline with the latest fixes and enhancements that the Apollo team have applied. Would really appreciate the PR. 👍
Any updates on this? I have problems updating to react 16.6.x which makes react-async-bootstrapper unusable.
@ctrlplusb Any idea when you'll be releasing this? We're using react-tree-walker
over at next-i18next. Thanks!
Seems like @ctrlplusb is inactive, it's a bit annoying. Did anyone create a fork and published it on npm?
@isaachinman @jcampalo Did you guys publish a fork by any chance?
Hi @oyeanuj. Over at next-i18next I had to move away from react-tree-walker
entirely due to:
- Performance impact.
- Unreconcilable issues with context.
It's a bit of a shame, but I think this project is self-aware in that it's not appropriate for every use case.
Good luck!
@isaachinman what did you end up using instead?
@oyeanuj I took a completely different approach that sidestepped tree traversal entirely. Sorry if that's not helpful.
@ctrlplusb Please give proper rights to others, we need this fix.
Shit. completely stuck on this. Days sunk into this already. What are people using as alternatives?
@mschipperheyn I didn't personally check it myself, but see this: https://github.com/ctrlplusb/react-tree-walker/pull/46#issuecomment-451268626
@mschipperheyn I didn't personally check it myself, but see this: #46 (comment)
I tested this and It did not solve this problem.
Does this fork fix the issue for anyone? It has worked for me. If so I can work on a PR w/ the fix I lifted from react-apollo
.
https://github.com/jaredLunde/react-tree-walker
React hooks is probably a bigger problem than the context API, a lot of libraries including graphql-react
and Apollo have given up on walking React trees.
Yeah, I mean it's a large part of why I haven't even begun looking at Hooks. I need asynchronous components on the server side more than I need hooks. Unfortunately React.lazy + Suspense is useless until their new server renderer is done in the second half of this year so this will have to do.
Ah, I see what you mean now. Your solution in graphql-react and the new react-apollo getDataFromTree are interesting. I imagine it's a lot more expensive but the reliability is what matters most I guess.
After the release of React Hooks I've moved away from react-tree-walker
.
I figured that it will be an endless fight to keep up with React. Very ambitious to say the least (just thinking about Suspense, Concurrent mode, etc.)
In the end I decided to go the same way as react-apollo
did. Even if it's one more complete render cycle. I simplified it a lot for my use case.
If anyone is interested that's how I've roughly done it.
// server.js
// Preparation for SSR
export const PreloadPromiseContext = React.createContext()
// This will called on page load on SSR
const preload = async tree => {
const loaders = []
const registerLoader = loaderConfig => loaders.push(loaderConfig)
// collect
ReactDOMServer.renderToStaticMarkup(
<PreloadPromiseContext.Provider value={{ registerLoader }}>
{tree}
</PreloadPromiseContext.Provider>
)
// resolve one after another
// loaders will put responses into global store
for (const { props, loader } of loaders) {
await loader(props)
}
}
// Actual SSR
server.use(async (req, res) => {
const app = (
<Provider store={store}>
<App />
</Provider>
)
await preload(app)
// use state and markup to render the actual HTML output
const state = store.getState()
const markup = ReactDOMServer.renderToString(app)
})
Now this is used in combination with a HoC (could maybe be a hook at some point?) Every component which should preload is wrapped with this.
import PreloadPromiseContext from './server.js'
const resolve = loader => Component => {
class Resolve extends React.Component {
static contextType = PreloadPromiseContext
constructor(props, context) {
super(props, context)
if (context && typeof window === 'undefined') {
// fetch on SSR
context.registerLoader({ props, loader })
} else {
// fetch on client side, to make life easier
// (you might not need this)
loader(props)
}
}
render() {
return <Component {...this.props} />
}
}
return hoistNonReactStatics(Resolve, Component)
}
// Example usage for a component. The result will be put in a global store inside the called function
export default presolve(props => props.store.fetchThings())(MyApp)