remix-run / remix

Build Better Websites. Create modern, resilient user experiences with web fundamentals.
https://remix.run
MIT License
29.77k stars 2.51k forks source link

I use useReducer to manage the data returned by loader. When handle is changed using <Link /> in the same route, useReducer data is not refreshed. #7235

Closed ZIA-Hans closed 1 year ago

ZIA-Hans commented 1 year ago

What version of Remix are you using?

latest version

Are all your remix dependencies & dev-dependencies using the same version?

Steps to Reproduce

  1. I have a Dynamic Route under which I manage the loaderData using useReducer.
  2. In this route, I using the component to change handle in this route.
  3. If I use the data of the useReducer agent to show, I find that the url and the data returned by the loader have changed, but the data of useReducer has not changed.
  4. If the user jumps from one route to another or use replace , the useReducer data will change.

see this example. https://stackblitz.com/edit/remix-run-remix-jxbyls

Expected Behavior

I'm using the Link component in a dynamic route to change the handle of the route, which triggers a change in the data in the useReducer.

Actual Behavior

I use the Link component in dynamic routing to change the handle of the route, and the data returned by the useReducer management loader does not get changed.

brophdawg11 commented 1 year ago

You need to change the reducer state by dispatching actions, so if you are trying to synchronize the loaderData to the reducer state you'll need to do it through an effect and a call to dispatch: https://stackblitz.com/edit/remix-run-remix-6g3jy8?file=app%2Froutes%2Fproducts.%24handle%2Froute.tsx

However, I would advise against trying to sync state this way and instead just use loaderData directly and let Remix manage the state for you. That's a core premise of using Remix is that it alleviates most needs for syncing your own state through useState/useEffect/useReducer.

ZIA-Hans commented 1 year ago

I have a route(products.$handle.tsx), it has some customized component use useFetcher to fetch data to display I discovered such a phenomenon, if I use component only change the handle, I expect each component will update thereby the page refresh. But in face, each component not fetch data, the page not refresh, I didn't expect that. Is this normal? If the component change route path or replace with tag to change handle in the same route , the page will refresh, this is very confusing to me.

The example

https://stackblitz.com/edit/remix-run-remix-jxbyls?file=app%2Fcomponents%2FFetcherDisplay.tsx,app%2Froutes%2Fapi.getData.tsx,app%2Froutes%2F_index.tsx

brophdawg11 commented 1 year ago

You have an empty array in your useEffect , so you are telling the fetcher not to re-run. If you want to re-run the fetcher when handle changes you need to put that in the dependencies:

export function FetcherDisplay(props: { handle: string }) {
  const fetcher = useFetcher();

  useEffect(
    () => fetcher.load(`/api/getData?handle=${props.handle}`),
    [props.handle]
  );

  return fetcher.state !== 'idle' ? (
    <div>fetching</div>
  ) : (
    <h1>Fetcher Data {fetcher.data?.name ?? 'nothing'}</h1>
  );
}

https://stackblitz.com/edit/remix-run-remix-97mhkc?file=app%2Fcomponents%2FFetcherDisplay.tsx

In the future, please open Q&A questions like this in the Remix Discord or the Discussions tab instead of commenting on closed issues 🙌