reactjsreact-routerreact-router-relay

Access React component from this.props.children with react-router


I have a website with the following react-router and react-router-relay setup:

main.jsx:

<Router history={browserHistory} render={applyRouterMiddleware(useRelay)}>
  <Route path="/:classroom_id" component={mainView} queries={ClassroomQueries}>
    <IndexRoute component={Start} queries={ClassroomQueries} />
    <Route path="tasks" component={TaskRoot} queries={ClassroomQueries}>
      <IndexRoute component={TaskList} queries={ClassroomQueries} />
      <Route path="task/:task_id" component={TaskView} queries={ClassroomTaskQueries} />
      <Route path="task/:task_id/edit" component={TaskEdit} queries={ClassroomTaskQueries} />
    </Route>
  </Route>
</Router>

The site is a virtual classroom, in which the teacher can create tasks for the students. These tasks can also be edited.

When a user edits a task in TaskEdit and hits save, the changes are applied and the user redirected to TaskList. Now I want to give the user a "Your changes were saved!" message once a task was edited.

I have created a bootstrap alert component (<Alert>) for this exact purpose.

For illustration purposes, assume I have the following mainView component (check Router above):

mainView.js:

render() {
  <div>
    <Menu />
    <Row>
      <Col md={3}>
        <Sidebar />
      </Col>
      <Col md={9}>
        {this.props.children}  <-- Start, TaskRoot, etc go here
      </Col>
    </Row>
    <Alert someProps={...} />  <-- Popup component
  </div>
}

Normally, I would create this alert component outside of the active component and just pass some kind of function that displays the alert as a prop to the active component. Similar to what is shown here.

However, this only works for a specific component. But because <Alert> sits at the root-level of my page (along with menubar, sidebar, and other things that are static on all pages), I cannot pass this as a prop due to {this.props.children}.

Allow me to illustrate the flow I want to achieve once more:

  1. User saves edits to a task in <TaskEdit>.
  2. Edits are saved and user taken back to <TaskList>.
  3. Immediately after, I want <Alert> to trigger, and show a message.

I've seen other solutions suggesting that you can clone all children of a React element and apply the prop needed to open the alert, but because of the nature of react-router this doesn't really seem to work.

How can I accomplish the above? Is there a way for a component to be globally accessible? Please note that I don't want to recreate <Alert> in each component. Only one such component for the whole DOM.


Solution

  • Simple answer:

    Put your Alert component once in your top level view, then use some pubsub or event bus lib to update it.

    In your Alert component onComponentDidMount function you listen for a specific event and data and update its state accordingly (visibility and message)

    In your Task save function you publish your event to the event bus.

    More advance answer:

    Use a React flux library https://facebook.github.io/flux/docs/overview.html