Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

159
Visualizações
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 Respostas
Responde à pergunta

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 Relatório

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 Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda