Search code examples
reactjsreact-hooksreact-routerreact-router-dom

Passing Data Between Route and Nested Route in React


Using a React Hooks and React Router v6, how do you load and define data on parent Events load and then pass subset of events data to the dynamic nested route -- campaign. I am planning to define and set state of Campaigns within the Events Route.

Since I am defining the routing on the index.js file, I am trying to see how to best pass this data from the parent Route to the nested Route, since the data is not yet defined in the index.js view.

<Route path="events" element={<Events />}>
  <Route path=":event" element={<Event campaign={campaign} />} />
</Route>

Solution

  • If you can't pass something via props, and it cannot be derived by the route params, you'd use context instead.

    In your example, <Event /> will be rendered where you place <Outlet />. You can wrap a context provider around your usage of <Outlet /> within the <Events /> component:

    const MyContext = React.createContext();
    const Events = () => {
      return <MyContext.Provider value={{greeting: "Hello, world!"}}>
        <Outlet />
      </MyContext.Provider>;
    };
    

    And then you can access this value within the event component by using that same context:

    const Event = () => {
      const { greeting } = React.useContext(MyContext);
      return <span>{greeting}</span>;
    };
    

    In order to make code easier to follow, where possible, I would recommend propagating information in the following preference order:

    1. Via props to the child component (either by passing the data itself, or some ID of the data which can be used to retrieve it from a data store)
    2. Have the child component determine the information from the URL
    3. Pass data from parent to child via context

    This is also in order of explicitness - be more explicit where possible.

    For your precise example, I would recommend using a combination of context and ids; each Campaign likely has its own ID, so I'd design an API that looked like this:

    const Events = () => {
      const campaignStore = useCampaignStore();
      return <CampaignContext.Provider value={campaignStore}>
        <React.Suspense fallback={<span>Loading...</span>}>
          <Outlet />
        </React.Suspense>
      <CampaignContext.Provider>
    }
    
    const Event = ({ campaignID }) => {
      const campaign = useCampaign(campaignID);
      ....
    }
    

    Where useCampaign would be some hook that would throw to enable suspense if a value was not yet available.