Is it possible to access child context from parent context?

Viewed 2106

I have an issue with Context. I have two contexts, AuthenticationProviderand VehiclesProvider.

App.tsx

import { AuthenticationProvider, VehiclesProvider } from '../contexts/

export class App extends React.Component {
  public render() {
    return (
      <ThemeProvider theme={theme}>
        <I18nextProvider i18n={i18nConfig}>
          <GlobalStyle />
          <AuthenticationProvider>
            <VehiclesProvider>
              <AppRouter />
            </VehiclesProvider>
          </AuthenticationProvider>
        </I18nextProvider>
      </ThemeProvider>
    )
  }
}

When we log out from the app the AuthenticationProvider updates its own state and resets it to initial state. But what we need to do is also, from a method in AuthenticationProvder, is to reset the state in VehiclesProvider.

So my question is: Is it possible from a Parent Context to access/modify state in a Child Context?

AuthenticationProvider.tsx

This is the method in AuthenticationProvider which should trigger a reset of the child context, VehiclesProvider.

export class AuthenticationProvider extends React.Component<
  IAuthenticationProviderProps,
  IAuthenticationProviderState
> {
  public state = {
    isAuthenticated: false,
    data: undefined,
  }

  // THIS METHOD MUST TRIGGER `resetFetchedVehicles` in `VehiclesContext`
  /**
  * **Update context to logged in state**
  *
  * Save response from API to context. Also let the app know that the user is authenticated.
  *
  * @memberof AuthenticationProvider
  */
  public login = (data: UserClass) => {
    const sameUser = this.isSameUser(data.Id)

    // If it is not the same user, then reset the VehcilesContext
    // if(!sameUser) this.props.resetFetchedVehicles()
    if(sameUser) this.METHODINCHILDCONTEXT.resetFetchedVehicles() // THIS IS WHERE THE STATE IN VEHICLESPROVIDER MUST BE MODIFIED

    this.setState({
      isAuthenticated: true,
      data,
    })
  }

  public render() {
    return (
      <VehiclesConsumer>
        {(context) => (
          <AuthenticationContext.Provider
            value={{
              isAuthenticated: this.state.isAuthenticated,
              data: this.state.data,
              login: this.login,
              logout: this.logout,
              isSameUser: this.isSameUser
            }}
          >
            {this.props.children}
          </AuthenticationContext.Provider>
        )}
      </VehiclesConsumer>
    )
  }
}

VehiclesContext.tsx

export class VehiclesProvider extends React.Component<
  IVehiclesProviderProps,
  IVehiclesProviderState
> {
  public state: IVehiclesProviderState = {
    loading: false,
    fetchedVehicles: [],
    totalVehicles: 0,
  }

  // THIS METHOD MUST BE TRIGGERED FROM `logout` in `AuthenticationContext`
  /**
   * **Remove all the fecthed vehicles from state**
   *
   * @memberof VehiclesProvider
   */
  resetFetchedVehicles = () => {
    this.setState({
      fetchedVehicles: []
    })
  }

  public render() {
    return (
      <VehiclesContext.Provider
        value={{
          loading: this.state.loading,
          fetchedVehicles: this.state.fetchedVehicles,
          fetchVehicles: async () => await this.fetchVehicles(),
          updateUserVehicleInfo: vehicle => this.updateVehicleInfo(vehicle),
          resetFetchedVehicles: () => null
        }}
      >
        {this.props.children}
      </VehiclesContext.Provider>
    )
  }
}

UPDATE

I have found a way to be able to do what I was asking for, with React Context. It is NOT a recommmended solution, since the code gets very ugly and hard to understand. But it works. I will still probably change to MST/Redux.

For those who are intersted, here is a Code Sandbox: Hi! Yesterday I asked a question regarding if it was possible to update a child context's state from a method in a parent context. I totally understand that it is now how React Context is supposed to be used.

But after a lot of struggling I have found a way to do it. It is a hacky way, and it is probably not recommended. If you need to update state from different components you should use Redux or MobxStateTree.

For those who are interested in how to be able to update a child context from a method in the partent, here is a Code Sandbox: https://codesandbox.io/s/kn16859z3

1 Answers

Maybe it's not an "answer", but it's to big to add it as a comment. I argee with @Treycos. It looks like you are trying to use context for purposes it was not designed for.

1) Context it is a way to pass state, props, etc. to the child components.

Example: Media player and a buttons.

MediaPlayer is a root component that knows everything about media and can play, pause, play next etc. PlayButton renders a button and use context to access play function of the root component. You don't care about visual hierarchy. PlayButton just needs to be inside MediaPlayer.

<MediaPlayer>
    <div>
        <div>My Media Player</div>
        <PlayButton>
    </div>
</MediaPlayer>

2) From your code it look like you need to manage something like global state. For this purpose you can use redux or something similar.

Related