Home > OS >  Managing clicked item's props in a new route - React router v6
Managing clicked item's props in a new route - React router v6

Time:09-11

I have a map of an objects. Each object is an item in a list. When I click on a concrete item it moves me to a specific route with that item's id. How to properly manage that concrete item's props inside new route?

Is this the way to go or there is better way?

export const Router = (): ReactElement => {
  const [itemDetails, setItemDetails] = useState<Item>();
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Root />}>
          <Route
            index
            element={<ItemList setItemDetails={setItemDetails} />}
          />
          <Route
            path="/item/:itemId"
            element={<ItemDetails itemDetails={itemDetails} />}
          />
        </Route>
      </Routes>
    </BrowserRouter>
  );
};

So basically what I'm doing here is I'm passing state setter function to a list where my mapped items are stored so when I click on a concrete one I save its props to that function.

Then I just pass updated state to a new route where details of a concrete item should be displayed.

CodePudding user response:

If you are asking if there's a better way than pushing some value from ItemList up to the parent state to be passed down to ItemDetails, then sure, a more React/react-router-dom way would be to hoist the items array from the ItemList component up to this parent component and pass the data down to children routes as props or via a context.

Example using props

export const Router = (): ReactElement => {
  const [items, setItems] = useState<Array<Item>>([]);

  ... logic to populate items state ...

  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Root />}>
          <Route index element={<ItemList items={items} />} />
          <Route path="/item/:itemId" element={<ItemDetails items={items} />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
};

ItemDetails

const ItemDetails = ({ items }) => {
  const { itemId } = useParams();
  const item = items.find(item => String(item.id) === itemId);

  if (!item) {
    return <div>No matching item found.</div>;
  }
  return (
    ... render item out to JSX
  );
};

Example using Outlet context

export const Router = (): ReactElement => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Root />}>
          <Route index element={<ItemList />} />
          <Route path="/item/:itemId" element={<ItemDetails />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
};

Root

const Root = () => {
  const [items, setItems] = useState<Array<Item>>([]);

  ... logic to populate items state ...

  return (
    ...
    <Outlet context={{ items }} />
    ...
  );
};

ItemList

const ItemList = () => {
  const { items } = useOutletContext();

  return (
    ... map items to JSX
  );
};

ItemDetails

const ItemDetails = () => {
  const { items } = useOutletContext();
  const { itemId } = useParams();
  const item = items.find(item => String(item.id) === itemId);

  if (!item) {
    return <div>No matching item found.</div>;
  }
  return (
    ... render item out to JSX
  );
};
  • Related