Working with React, Material-UI, and React-Router, and I am trying to use React-Router in conjunction with Material-UI tabs. In fact I have it all working, but with one caveat. If a user uses a route to load a specific page, the Tabs
does not show the correct tab as selected. It all works once the user is in the app; they can click tabs and the app does all three things correctly (loads the correct page, shows the correct tab selected, shows the correct URL and path), but if they type in a path directly into their browser the first tab is selected.
App:
function App() {
const [page, setPage] = useState("1");
const handleChange = (e: React.SyntheticEvent, newPage: string) => {
setPage(newPage);
};
return (
<Router>
<div className="App">
<Header title="My Site" />
<TabContext value={page}>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<Tabs
aria-label="Tab navigation"
onChange={handleChange}
value={page}
variant="fullWidth"
>
<Tab label="Tab One" component={Link} to="/pageOne" value="1" />
<Tab label="Tab Two" component={Link} to="/pageTwo" value="2" />
<Tab label="Tab Three" component={Link} to="/pageThree" value="3" />
<Tab label="Tab Four" component={Link} to="/pageFour" value="4" />
</Tabs>
</Box>
</TabContext>
<Footer />
<Routes>
<Route
path="/"
element={<pageOne />}
/>
<Route
path="/pageOne"
element={<pageOne />}
/>
<Route path="/pageTwo" element={<pageTwo />} />
<Route path="/pageThree" element={<pageThree />} />
<Route path="/pageFour" element={<pageFour />} />
</Routes>
</div>
</Router>
);
}
This all works as expected, since the initial page
state defaults to "1"
. That state variable page
is a holdover from when I just had tabs and not react-router
; I am not sure if I need to get rid of that.
I've been trying to figure out how to determine what that value should be based on the entered path, but parsing the window.location.href
string and setting the default to {[conditional goes here]}
is not working: I get an error from React telling me that a condition like pageString === "pageTwo"
will always return false, since JS is comparing references and not values.
Currently using react@18.2.0
, mui@5.12.3
, and react-router-dom@6.11.0
.
You can use the URL path as the source of truth for which tab should be active/selected. In other words, don't duplicate the "tab state" when you can let react-router-dom
manage the "state". When the App
component renders, for any reason, the active/selected tab will automagically be synchronized to the current route path value.
Use the useLocation
hook to access the current pathname
value and pass this as the Tabs
component's value
prop value, and update all the Tab
components to use a path value instead of the string values "1"
through "4"
. The Router
will need to be promoted higher in the ReactTree so that App
can use the useLocation
hook.
Example:
export default function App() {
const { pathname } = useLocation();
return (
<div className="App">
<Header title="My Site" />
<TabContext value={pathname}>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<Tabs
aria-label="Tab navigation"
value={pathname}
variant="fullWidth"
>
<Tab label="Tab One" component={Link} to="/pageOne" value="/pageOne" />
<Tab label="Tab Two" component={Link} to="/pageTwo" value="/pageTwo" />
<Tab label="Tab Three" component={Link} to="/pageThree" value="/pageThree" />
<Tab label="Tab Four" component={Link} to="/pageFour" value="/pageFour" />
</Tabs>
</Box>
</TabContext>
<Footer />
<Routes>
<Route index element={<Navigate to="/pageOne" />} />
<Route path="/pageOne" element={<PageOne />} />
<Route path="/pageTwo" element={<PageTwo />} />
<Route path="/pageThree" element={<PageThree />} />
<Route path="/pageFour" element={<PageFour />} />
</Routes>
</div>
);
}
import { BrowserRouter as Router } from "react-router-dom";
...
<Router>
<App />
</Router>