react-router icon indicating copy to clipboard operation
react-router copied to clipboard

[Feature]: <Routes> onChange props please

Open AntiMoron opened this issue 3 years ago • 0 comments

What is the new or updated feature that you are suggesting?


interface RouterState {
  location: Location;
  params: Params;
}

interface RoutesProps {
  children?: React.ReactNode;
  location?: Partial<Location> | string;
  // I'm going to add this
  onChange?: (prevState: RouterState, nextState: RouterState) => void;
}

<Routes onChange={(prevState, nextState) => {
   // prevState.location; same as useLocation
  // prevState.params; same as useParams
}}>
  <Route element={<XX1 />} route="/xxx1" />
  <Route element={<XX2 />} route="/xxx2" />
  <Route element={<XX3 />} route="/xxx3" />
</Routes>

Why should this feature be included?

What is not enough?

With the current API, you cannot detect path changing before it actually happens. You can only know it afterwards with useLocation and useEffect.

Why I need it, show some scenarios:

Usually, we need to know when the route changes as we need to fetch API results with different parameters. We'll code like this:

// <Route  element={<DetailPage />} path="/item/:itemId" />
// localhost:3000/#/item/10001?filters={"price":">100$"}

const location = useLocation();
const params = useParams();

useEffect(() => {
  const itemId =  params.itemId;
  const pathname = location.pathname;
  // get extra params from hash query, pass it as a request parameter.
  request('/some/api/for/itemDetial', parse(location.search).filters).then((data) => { // filters as a parameter.
    setState(data);
  });
}, [location, params]);

As there's some new data flow library as recoil by Facebook which is based on observer pattern, requests are usually bound with state changes, like:

// atom.ts
const filter = atom({ key: 'filterParamKey', default: undefined });
const getDetail = selector({
  key: 'getDetailSelector',
  get: async ({ get }) => {  // this function will be triggered for every atom involved in with the `get` method changes.
    const filterValue = get(filter);
    return await request('/some/api/for/itemDetial', filterValue);
  }
});

// component.tsx
const location = useLocation();
const params = useParams();
const setFilterState = useSetRecoilState(filter); // components just need to focus on setting values with `recoil`.
useEffect(() => {
  setFilterState(parse(location.search).filters);
}, [location, params]);

In this case, request is triggered instantly when the filter state changes. If you route as below:

  1. navigate to #/item/10001?filters=xxxx
  2. navigate to #/randompage // which clears the filter state.
  3. click prev of the browser and find out filter parameter is lost.

How to fix it with onChange property

const location = useLocation();
const params = useParams();
const setFilterState = useSetRecoilState(filter); // components just need to focus on setting values with `recoil`.
return <Routes onChange={(prev, next) => {
  setFilterState(parse(next.location.search).filters);
}}>

</Routes>

AntiMoron avatar Dec 06 '22 13:12 AntiMoron