I’m fairly new to React and am following a tutorial to train myself.
The problem I’m having is that I have several routes rendering the same component with different props, but the component doesn’t seem to be mounted/dismounted when navigating using NavLink. So my custom hook isn’t triggered and it’s as if we haven’t navigated.
If I manually refresh the page after navigation, the component is correctly updated.
I use [email protected]
and [email protected]
.
index.tsx
import React from 'react'; import ReactDOM from 'react-dom/client'; import { BrowserRouter } from "react-router-dom"; import './index.css'; import App from './App'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode> );
App.tsx
import React from 'react'; import { Header } from "./components"; import { Route, Routes } from "react-router-dom"; import { MovieList } from "./pages"; function App() { return ( <> <Header /> <Routes> <Route path="/" element={<MovieList />} /> <Route path="/movies/bottom" element={<MovieList sort='bottom' />} /> <Route path="/movies/unpopular" element={<MovieList sort='unpopular' />} /> <Route path="/movies/upcoming" element={<MovieList sort='upcoming' />} /> </Routes> </> ); } export default App;
- Into the
Header.tsx
component, I have :<li> <NavLink to="/" className={linkClass} aria-current="page" end>Recent</NavLink> </li> <li> <NavLink to="/movies/unpopular" className={linkClass}>Unpopular</NavLink> </li> <li> <NavLink to="/movies/bottom" className={linkClass}>Bottom Rated</NavLink> </li> <li> <NavLink to="/movies/upcoming" className={linkClass}>Upcoming</NavLink> </li>
MovieList.tsx
import { useState } from "react"; import { Card } from "../components"; import { MovieService } from "../services/Movie.service"; type MovieListProps = { sort?: MovieService.defaultSort; } export function MovieList({ sort }: MovieListProps = {}) { const [filter, setFilter] = useState({ defaultSort: sort }); const movies = MovieService.useGet(filter); return ( <main> <button onClick={() => setFilter({defaultSort: 'now_playing'})}>Recent</button> <button onClick={() => setFilter({defaultSort: 'unpopular'})}>unpopular</button> <button onClick={() => setFilter({defaultSort: 'bottom'})}>Bottom Ratted</button> <button onClick={() => setFilter({defaultSort: 'upcoming'})}>Upcoming</button> <section className="flex flex-wrap justify-center gap-4"> { movies.data?.results.map((movie, index) => ( <Card key={index} item={movie}/> )) } </section> </main> ); }
Here, the movies
object isn’t updated when I click on the NavLink from the header, but I’ve added some buttons that call setFilter, and when I click on those buttons, it works fine, why?
You can find the code of the tutorial that works here : github
I have diff because I’m doing it with Typescript and I isolated the code to fetch the APIs in a different way, you can find my currently code here : github
But I still don’t understand why my implementation doesn’t work.
2
Answers
React-Router optimizes to keep React components mounted. In other words, it tries to not do any extra work to unmount and remount the same React component if it can help it. In React, triggering a component rerender is less effort than remounting.
You can add a
useEffect
hook to update thefilter
state when thesort
prop value changes:Or you could give each "instance" of the
MovieList
component a unique React key so the component remounts when the route changes.The updates are not seen as the
.map
will never reflect them because theconst movies
you are trying to map are not in a state variable managed by theuseState
hook, so any changes in movies value after the initial render are being ignored. In order to solve this issue, you should create auseState
hook, and then get the data (presumably a fetch so make sure to use async/await) and set the state value so that your.map
function would rerender on a state change.When you update the filter state value with setFilter, it causes the component to rerender, and your updated
const movies
becomes visable as by that time it has a changed value.