I'm currently working on a react project where I'm sending data between routes. I've looked into context managers in React, but from what I can tell they are mainly used for wrapping components, and not getting and setting data.
Is there any simple way to initialize a project-global state Object = {}
which can be written-to- and read-from easily throughout the project routes?
Here is a little example structure which emulates what I would like for this to look like:
index.tsx
:
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import App1 from "./App1";
import App2 from "./App2";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
const [globalNumber, setGlobalNumber] = // initialize some global counter at zero.
root.render(
<React.StrictMode>
<Router>
<Routes>
<Route path="/app1" element={<App1 />} />
<Route path="/app2" element={<App2 />} />
</Routes>
</Router>
</React.StrictMode>
);
App1.tsx
:
const App1 = () => {
const [globalNumber, setGlobalNumber] = // get state-setter and current value from global state
return (
<button
onClick={() => {
setGlobalNumber(globalNumber 1);
}}
>
Click me to increment global number!
</button>
);
};
export default App1;
App2.tsx
:
const App2 = () => {
const [globalNumber, setGlobalNumber] = // get current global number
return <h1>Global number is currently: {globalNumber}</h1>;
};
export default App2;
Here I'm using some imaginary [globalNumber, setGlobalNumber]
state, and commented where there's code missing. The hope is then to be able to navigate to route /app1
, click the button a few times, and have the number displayed on screen when navigating to route /app2
. Thanks!
CodePudding user response:
You are on the right track with using a React context. React Context providers are a way to pass "props" down to distant descendent component without explicitly drilling them down as props. These can be any value. It's common to provide some state and an updater function via a Context provider.
react-router
's Outlet
component actually provides a context for just this use case of passing data to nested route components.
Example:
import React from "react";
import ReactDOM from "react-dom/client";
import {
BrowserRouter as Router,
Route,
Routes,
Outlet // <-- import Outlet
} from "react-router-dom";
import App1 from "./App1";
import App2 from "./App2";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
const App = () => {
const [globalNumber, setGlobalNumber] = React.useState(0);
return (
<Router>
<Routes>
<Route // <-- layout route
element={(
<Outlet
context={{ // <-- pass context value
globalNumber,
setGlobalNumber
}}
/>
)}
>
<Route path="/app1" element={<App1 />} />
<Route path="/app2" element={<App2 />} />
</Route>
</Routes>
</Router>
);
};
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Then in the nested route components use the useOutletContext
hook to access the context value.
import { useOutletContext } from 'react-router-dom';
const App1 = () => {
const { setGlobalNumber } = useOutletContext();
return (
<button
onClick={() => {
setGlobalNumber(globalNumber => globalNumber 1);
}}
>
Click me to increment global number!
</button>
);
};
...
import { useOutletContext } from 'react-router-dom';
const App2 = () => {
const { globalNumber } = useOutletContext();
return <h1>Global number is currently: {globalNumber}</h1>;
};