ctrlplusb / react-tree-walker

Walk a React (or Preact) element tree, executing a "visitor" function against each element.
MIT License
345 stars 34 forks source link

fixing React.createContext in SSR #40

Open tadeuszwojcik opened 6 years ago

tadeuszwojcik commented 6 years ago

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!

jaydenseric commented 5 years ago

As another reference, I've recently fixed the graphql-react SSR preload function for React v16.6 context: Diff here.

ctrlplusb commented 5 years ago

@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. 👍

jcampalo commented 5 years ago

Any updates on this? I have problems updating to react 16.6.x which makes react-async-bootstrapper unusable.

isaachinman commented 5 years ago

@ctrlplusb Any idea when you'll be releasing this? We're using react-tree-walker over at next-i18next. Thanks!

jcampalo commented 5 years ago

Seems like @ctrlplusb is inactive, it's a bit annoying. Did anyone create a fork and published it on npm?

oyeanuj commented 5 years ago

@isaachinman @jcampalo Did you guys publish a fork by any chance?

isaachinman commented 5 years ago

Hi @oyeanuj. Over at next-i18next I had to move away from react-tree-walker entirely due to:

  1. Performance impact.
  2. 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!

oyeanuj commented 5 years ago

@isaachinman what did you end up using instead?

isaachinman commented 5 years ago

@oyeanuj I took a completely different approach that sidestepped tree traversal entirely. Sorry if that's not helpful.

jcampalo commented 5 years ago

@ctrlplusb Please give proper rights to others, we need this fix.

mschipperheyn commented 5 years ago

Shit. completely stuck on this. Days sunk into this already. What are people using as alternatives?

dan-lee commented 5 years ago

@mschipperheyn I didn't personally check it myself, but see this: https://github.com/ctrlplusb/react-tree-walker/pull/46#issuecomment-451268626

jdwillemse commented 5 years ago

@mschipperheyn I didn't personally check it myself, but see this: #46 (comment)

I tested this and It did not solve this problem.

jaredLunde commented 5 years ago

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

jaydenseric commented 5 years ago

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.

jaredLunde commented 5 years ago

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.

jaredLunde commented 5 years ago

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.

dan-lee commented 5 years ago

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. ```jsx // 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( {tree} ) // 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 = ( ) 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. ```jsx 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 } } 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) ```