I have two components, one called <Flights/>
and the other called <FlightResults/>
.
Flights renders a context provider for the rest of the application.
const Flights = () => {
return (
<FlightSearchContext.Provider
value={{
typeOfTrip,
fromAirport,
departureDate,
returnDate,
toAirport,
outGoingFlights,
searchAirports,
setSearchAirports,
}}
>
<h1>Some UI</h1>
</FlightSearchContext.Provider>
);
};
export default Flights;
const FlightResults = () => {
const { toAirport, outGoingFlights } = useContext(FlightSearchContext);
return (
<div>Flight results</div>
)
}
export default FlightResults
Right now I'm declaring both routes in my index.tsx
file like this.
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { Routes, Route } from "react-router";
import { BrowserRouter } from "react-router-dom";
import Flights from "./Components/Flights/Flights";
import FlightResults from "./Components/Flights/FlightResults";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route path="/flights" element={<Flights />} index />
<Route path="/flights/flight-results" element={<FlightResults />} />
</Route>
</Routes>
</BrowserRouter>
</React.StrictMode>
);
FlightResults
is a child route of Flights
and it needs to access the context data that is declared in the Flights
component.
Currently everything works fine but FlightResults
cant access the data in the Flights
component.
After a lot of searching around i found out that I should wrap my child components in the index.tsx
file as below.
<Route path="/" element={<App />}>
<Route path="/flights" element={<Flights />} index>
<Route path="/flight-results" element={<FlightResults />} />
</Route>
<Route path="/hotels" element={<Hotel />} />
<Route path="/taxi" element={<Taxi />} />
</Route>
and whilst this works the FlightResults
UI doesn't show under /flights/flightresults
, a possible fix was to render an Outlet
component on the Flights
component like below
const Flights = () => {
return (
<FlightSearchContext.Provider
value={{
typeOfTrip,
fromAirport,
departureDate,
returnDate,
toAirport,
outGoingFlights,
searchAirports,
setSearchAirports,
}}
>
<h1>Some UI</h1>
<Outlet/>
</FlightSearchContext.Provider>
);
};
export default Flights;
and the above does work but now both UIs show up on /flights/flightresults/
.as in both Flights
and FlightResults
show up on the same URL.
How can I make the right component render on the right URL but also still access the context data?
CodePudding user response:
Short answer: you should not declare FlightSearchContext.Provider
in Flights
, but instead above both Flights
and FlightResults
.
Adapting this example from the docs, you can declare the context above both Route
, and if you need to update data in Flights
, be sure to include setWhateverData
in the context so you can update it from any children of the context
CodePudding user response:
Issue
The issue is that you're mixing what you want exclusively to be rendered on "/flights"
with data you want provided to multiple routes.
Solution
The FlightSearchContext.Provider
component of the current Flights
component should refactored to a Layout Route leaving the UI portion in Flights
to be rendered on its own route.
Example:
const FlightsProvider = () => (
<FlightSearchContext.Provider
value={{
typeOfTrip,
fromAirport,
departureDate,
returnDate,
toAirport,
outGoingFlights,
searchAirports,
setSearchAirports,
}}
>
<Outlet/>
</FlightSearchContext.Provider>
);
<Route path="/flights" element={<FlightsProvider />}>
<Route
index // <-- "/flights"
element={<Flights />}
/>
<Route
path="flight-results" // <-- "/flights/flight-results"
element={<FlightResults />}
/>
</Route>
If you are really wanting to not use an Outlet
component then you can simply lift the FlightSearchContext.Provider
higher in the ReactTree. In this case the FlightsProvider
becomes a normal Wrapper component.
Example:
const FlightsProvider = ({ children }) => (
<FlightSearchContext.Provider
value={{
typeOfTrip,
fromAirport,
departureDate,
returnDate,
toAirport,
outGoingFlights,
searchAirports,
setSearchAirports,
}}
>
{children}
</FlightSearchContext.Provider>
);
root.render(
<React.StrictMode>
<BrowserRouter>
<FlightsProvider>
<Routes>
<Route path="/" element={<App />}>
<Route path="/flights" >
<Route index element={<Flights />} />
<Route path="flight-results" element={<FlightResults />} />
</Route>
</Route>
</Routes>
</FlightsProvider>
</BrowserRouter>
</React.StrictMode>
);