react-tree-walker icon indicating copy to clipboard operation
react-tree-walker copied to clipboard

fixing React.createContext in SSR

Open tadeuszwojcik opened this issue 5 years ago • 18 comments

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!

tadeuszwojcik avatar Aug 24 '18 07:08 tadeuszwojcik

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

jaydenseric avatar Oct 29 '18 15:10 jaydenseric

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

ctrlplusb avatar Oct 29 '18 16:10 ctrlplusb

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

jcampalo avatar Nov 28 '18 11:11 jcampalo

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

isaachinman avatar Dec 14 '18 20:12 isaachinman

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

jcampalo avatar Dec 17 '18 11:12 jcampalo

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

oyeanuj avatar Dec 29 '18 01:12 oyeanuj

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!

isaachinman avatar Dec 29 '18 14:12 isaachinman

@isaachinman what did you end up using instead?

oyeanuj avatar Dec 29 '18 15:12 oyeanuj

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

isaachinman avatar Dec 29 '18 15:12 isaachinman

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

jcampalo avatar Jan 21 '19 08:01 jcampalo

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

mschipperheyn avatar Jan 23 '19 16:01 mschipperheyn

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

dan-lee avatar Jan 23 '19 16:01 dan-lee

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

I tested this and It did not solve this problem.

jdwillemse avatar Jan 31 '19 17:01 jdwillemse

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

jaredLunde avatar Feb 12 '19 03:02 jaredLunde

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.

jaydenseric avatar Feb 12 '19 03:02 jaydenseric

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 avatar Feb 12 '19 03:02 jaredLunde

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.

jaredLunde avatar Feb 12 '19 03:02 jaredLunde

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)

dan-lee avatar Feb 12 '19 10:02 dan-lee