reactjs - Cómo compartir datos entre dos componentes sin usar funciones de orden superior en React Router y outlet
Tengo dos componentes, uno llamado <Flights/> y el otro llamado <FlightResults/>.
Flights proporciona un proveedor de contexto para el resto de la aplicación.
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
En este momento estoy declarando ambas rutas en mi archivo index.tsx así.
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 es una ruta hija de Flights y necesita acceder a los datos del contexto que se declaran en el componente Flights.
Actualmente todo funciona bien, pero FlightResults no puede acceder a los datos en el componente Flights.
Después de buscar mucho, descubrí que debería envolver mis componentes hijos en el archivo index.tsx como se muestra a continuación.
<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>
y aunque esto funciona, la interfaz FlightResults no se muestra bajo /flights/flightresults, una posible solución fue renderizar un componente Outlet en el componente Flights como se muestra a continuación
const Flights = () => {
return (
<FlightSearchContext.Provider
value={{
typeOfTrip,
fromAirport,
departureDate,
returnDate,
toAirport,
outGoingFlights,
searchAirports,
setSearchAirports,
}}
>
<h1>Some UI</h1>
<Outlet/>
</FlightSearchContext.Provider>
);
};
export default Flights;
y lo anterior funciona pero ahora ambas interfaces de usuario aparecen en /flights/flightresults/, es decir, tanto Flights como FlightResults aparecen en la misma URL.
¿Cómo puedo hacer que el componente correcto se renderice en la URL correcta pero también acceder a los datos del contexto?
Respuesta #1
Problema
El problema es que estás mezclando lo que quieres que se represente exclusivamente en "/flights" con datos que quieres que se proporcionen a múltiples rutas.
Solución
El componente FlightSearchContext.Provider del componente actual Flights debe ser refactorizado a una Ruta de Diseño dejando la parte de la interfaz de usuario en Flights para ser renderizada en su propia ruta.
Ejemplo :
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>
Si realmente quieres no usar un componente Outlet, entonces puedes simplemente elevar el FlightSearchContext.Provider más alto en el ReactTree. En este caso, el FlightsProvider se convierte en un componente contenedor normal.
Ejemplo :
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>
);
Respuesta #2
Respuesta corta: no deberías declarar FlightSearchContext.Provider en Flights, sino más bien por encima de Flights y FlightResults.
Adaptando este ejemplo de la documentación, puedes declarar el contexto por encima de ambos Route, y si necesitas actualizar datos en Flights, asegúrate de incluir setWhateverData en el contexto para que puedas actualizarlo desde cualquiera de los hijos del contexto
