Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

157
Vistas
React component with two dependent useEffect hooks

What follows is my first attempt at the component, which is supposed to re-query data when the filters prop changes. Additionaly, whenever such a change is detected it should wrap back to the first page (first occurence of useEffect). Thirdly, the user should be able to go to the next page manually (nextPage callback).

function Fetcher(filters) {
  const [page, setPage] = React.useState(0);

  const nextPage = React.useCallback(() => {
    setPage((p) => p + 1);
  }, []);

  React.useEffect(() => {
    setPage(0);
  }, [filters]);

  React.useEffect(() => {
    externalRequest(filters, page);
  }, [filters, page]);

  return <button onClick={nextPage}></button>;
}

Unfortunately this will naturally won't work properly, as resetting the page in the first effect hook will be performed asynchronously and picked up belatedly: if the user sits on page 1 and filters change, externalRequest will be fired twice in the following manner:

  1. externalRequest(1, newFilters)
  2. externalRequest(0, newFilters)

What I'd consider to be a workaround is either to store the page as a mutable reference as follows:

function Fetcher(filters) {
  const page = React.useRef(0);

  const nextPage = React.useCallback(() => {
    page.current = page.current + 1;
  }, []);

  React.useEffect(() => {
    page.current = 0;
  }, [filters]);

  React.useEffect(() => {
    externalRequest(filters, page);
  }, [filters, page.current]);

  return <button onClick={nextPage}></button>;
}

or lift it up and pass from the outside as the prop (which is not ideal, as I want to avoid leaking this dependency outside). The issue with the former solution (using useRef) is that the component won't re-render when nextPage is called and the re-cycle would need to be forced. The problem with the latter is that it puts the 'burden' of managing the page dependency outside. It solves it though. My question is whether there exists a way to structure the Fetcher so that we get the best of both worlds i.e. page state internally in the component and it remaining part of the state, rather than a reference? I'm sorry if this example seems a bit contrived, but I feel this pattern occurs quite often and I'd be really grateful for some input on the matter!

about 4 years ago · Juan Pablo Isaza
2 Respuestas
Responde la pregunta

0

You can resolve this issue by removing the filters from the second effect dependency

Why? since you are making sure the page will trigger re-render for effect, so, by setting the filter in both effects, that means trigger twice, one for filter and then page also updates so this second render will be trigger.

You can check the demo here.

function Fetcher(props) {
  const [page, setPage] = useState(0);

  const nextPage = useCallback(() => {
    setPage((p) => p + 1);
  }, []);

  useEffect(() => {
    setPage(0);
  }, [props.filters]);

  useEffect(() => {
    console.log(props.filters, page);
  }, [page]);

  return <button onClick={nextPage}>Next Page</button>;
}

There's an important note: each dependency will trigger re-render when its update, so that we put it inside the dependency array, but you need to make sure about the rendering tree too.

Also, you have another solution, by saving the old filter and checking the new value if it's equal old one or not.

Also, you can use the re-structure component and build function and call what you needed based on conditions...

And I don't recommend to useRef in this case... it's not a solution for a normal state, and I think the first solution is fair.

UPDATE 1: (for taking care about filters change):

function Fetcher(props) {
  const [page, setPage] = useState(0);
  const [oldFilters, setOldFilters] = useState(props.filters);

  const nextPage = useCallback(() => {
    setPage((p) => p + 1);
  }, []);

  useEffect(() => {
    setPage(0);

    if(JSON.stringify(props.filters) !== JSON.stringify(oldFilters)){
      setOldFilters(props.filters);
    }
  }, [props.filters]);

  useEffect(() => {
    console.log(props.filters, page);
  }, [page, oldFilters]);

  return <button onClick={nextPage}>Next Page</button>;
}

Also, the setPage(() => () => 0); its a good option (like Pasato answer bellow) for handling this cae by update referance each time to trigger re-render.

about 4 years ago · Juan Pablo Isaza Denunciar

0

Thank you Anees, that was so simple.. There's one edge caveat that's not addressed by your solution and it's the case in which filters change, while still on page 0 - in this case page dependent effect won't fire. I've ultimately opted in for the following:

function Fetcher(props) {
  const [page, setPage] = useState(() => () => 0);

  const nextPage = useCallback(() => {
    setPage((p) => () => p() + 1);
  }, []);

  // do not run on the mount, but only when props change
  // as otherwise the initial request is fired twice 
  useUpdate(() => {
    setPage(() => () => 0);
  }, [props.filters]);

  useEffect(() => {
    const actualPage = page();
  }, [page]);

  return <button onClick={nextPage}>Next Page</button>;
}

Laziness of the state is irrelevant in this case - I just "wrap it" in the function so that two page instances are referentially different, even if they return the same number. Also, page resetter doesn't run upon mount so that we don't query twice.

about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda