Saif
Saif

Reputation: 3452

React router does not reload component if same element is used for two paths

I have the following two paths set up

<Route path="bookings/:bookingnumber" element={<Bookings />} />
<Route path="bookings" element={<Bookings />} />

In Bookings component I have conditional code written to check if a parameter :bookingnumber is passed, and then it only shows results relevant to that booking number. Otherwise, if no parameter is passed, it shows all the results for all the bookings.

I am using Outlet to display these components in the main area, and the two paths can be chosen from a sidebar.

Let's say I have 10 results if I go to /bookings, and 2 results if I go to /bookings/123.

However, if I go to /bookings/123 first and then to /bookings it keeps on showing only 2 results (the component does not reload, however I can see the URL changing in the browser)

Upvotes: 13

Views: 6034

Answers (5)

iduniq
iduniq

Reputation: 61

ok, the solution is add a unique key property value to the component, for example:

<Route path="bookings/:bookingnumber" element={<Bookings key={"BookingsA"}/>} />
<Route path="bookings" element={<Bookings key={"BookingsB"} />} />

Upvotes: 0

erFra17
erFra17

Reputation: 133

I've been struggling too with this issue, the solution is to add the key prop, which is used by react to understand when 2 components are different (otherwise it assumes they're the same and they will not be re-rendered).

Hence the solution in your case may look like:

<Route key={'single'} path="bookings/:bookingnumber" element={<Bookings />} />

<Route key={'all'} path="bookings" element={<Bookings />} />

Upvotes: 7

Drew Reese
Drew Reese

Reputation: 202751

Issue

<Route path="bookings" element={<Bookings />} />
<Route path="bookings/:bookingnumber" element={<Bookings />} />

With the same component rendered on more than 1 route, if you navigate from one route to another for the same routes component, the routed component remains mounted.

Example: If navigating from "/bookings" to "/bookings/123", or "/bookings/123" to "/bookings", or "/bookings/123" to "/bookings/456" the Bookings component remains mounted. If you've any logic that depends on the bookingnumber route path parameter then the Bookings component needs to handle this in a useEffect hook with a proper dependency on the bookingnumber param.

Solution

Use a useEffect hook to "listen" for changes to the bookingnumber param value.

Example:

const Bookings = () => {
  const { bookingnumber } = useParams();

  useEffect(() => {
    // initial render or booking number value updated
    // run logic depending on bookingnumber value to update "results"
    ... 
  }, [bookingnumber]);

  ...
};

Upvotes: 9

Adnane Ar
Adnane Ar

Reputation: 683

Hello I believe you are using react-router-dom v6, in this case you can wrap the child routes inside your main path route. And then give them the index(true|false) attribute to make sure the router understand if it is the exact index page or should render a page based on params.

Note: I copied the example from above, and added some lines to fix it!

<Route path="bookings">
  <Route index={false} path=":bookingnumber" element={<ViewBooking />} />
  <Route index={true} element={<BookingList />} />
</Route>

UPDATE: You can handle a single component route with useParams hook.

Here what you're routing file should look like:

<Route path="bookings">
  <Route index={false} path=":bookingnumber" element={<ViewBooking />} />
  <Route index={true} element={<BookingList />} />
</Route>

And this is what your component should look like:

const MyComponent = () => {

  const { bookingnumber } = useParams()
  
  // if the path doesn't include the booking number then we should just render a normal page
  if( !bookingnumber ) {
    return <h1>This is a simple page</h1>
  }
  
  
  // if the page path does include the booking number then we can fetch data and return another result
  // example you can fetch data based on this number
  const [username, setUsername] = useState("")
  useEffect(() => {
    const fetchData = async () => {
      const req = await fetch(`https://629c770de9358232f75b55dc.mockapi.io/api/v1/users/${bookingnumber}`)
      const data = await req.json()
      setUsername(data.name)
    }
    fetchData()
  }, [username])

  return (
    <h1>Welcome mr.{username}</h1>
  )
}

Upvotes: 1

Meet Majevadiya
Meet Majevadiya

Reputation: 365

<Route path="bookings" element={<BookingList />}>
  <Route path=":bookingnumber" element={<ViewBooking />} />
</Route>

make two pages, first one will show all list of bookings and second one will show ID data

Upvotes: 1

Related Questions