React context update does not cause component rerender

1k Views Asked by At

I'm creating a code splitting solution for react app using react router, webpack and dynamic imports. The idea is to map all the routes and corresponding components to different app contexts and split code according to app contexts. When user visits some route the whole code chunk of related app context is being loaded.

Code examples:

App.tsx:

class App extends React.Component<Props, State> {
  render() {    
    if (this.props.data.loading || this.props.localData.loading) {
      return <Loader />
    }

    return (
      <IntlProvider locale={this.props.localData.session.locale} messages={this.state.translations}>
        <Router history={history}>
            <Route
              exact
              path={`/screens/:action?`}
              render={() => <ComponentLoader contextName={Context.Screens} componentName={'ScreenList'} />}
            />
        </Router>
      </IntlProvider>
    )
  }
}

export default withData(App)

ComponentLoader.tsx:

export enum Context {
  Screens = 'Screens',
  Channels = 'Channels'
}

const CONTEXT_LOADERS: { [name: string]: ComponentChunkLoader } = {
  [Context.Screens]: () => import('../../routerContexts/screens'),
  [Context.Channels]: () => import('../../routerContexts/channels'),
}

const loadedContexts: ContextsCollection = {}

class ComponentLoader extends React.PureComponent<Props, State> {

  state: State = {
    Component: null
  }

  async componentDidMount() {
    this._updateComponent(this.props)
  }

  async componentDidUpdate(prevProps: Props, prevState: State) {
    if (this.props.componentName !== prevProps.componentName || this.props.contextName !== prevProps.contextName) {
      this._updateComponent(this.props)
    }
  }

  _updateComponent = async (props: Props) => {
    let module = loadedContexts[props.contextName]
      ? loadedContexts[props.contextName]
      : await CONTEXT_LOADERS[props.contextName]()

    if (!loadedContexts[props.contextName]) loadedContexts[props.contextName] = module

    let ComponentClass = module[props.componentName]

    this.setState({
      Component: ComponentClass
    })
  }

  render() {

    if (this.state.Component !== null) {
      return <this.state.Component />
    }

    return <Loader />
  }
}

export default ComponentLoader

So when I switch to different routes, I see all the components correctly, and code splitting works correctly.

The problem is: when the data in react context updates, ScreenList component in the example doesn't get updated. When I pass ScreenList directly to react router Route everything works well. So the problem is in my ComponentLoader component that I use for code splitting.

Any ideas what can be the reason?

UPDATE: Alrite, what i figured out now: if I wrap my ComponentLoader in a HOC that injects some data from context (like export default withRouter(ComponentLoader)) , everything works well and components rerender as expected. Why is happening like this?

0

There are 0 best solutions below