Lo que sigue es mi primer intento en el componente, que se supone que vuelve a consultar los datos cuando cambia la propiedad de filtros. Además, siempre que se detecte un cambio de este tipo, debería volver a la primera página (primera aparición de useEffect ). En tercer lugar, el usuario debería poder ir a la página siguiente manualmente (devolución de llamada nextPage ).
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>; } Desafortunadamente, esto, naturalmente, no funcionará correctamente, ya que el restablecimiento de la página en el primer gancho de efecto se realizará de forma asíncrona y se recuperará con retraso: si el usuario se sienta en la página 1 y los filtros cambian, externalRequest se activará dos veces de la siguiente manera:
externalRequest(1, newFilters)externalRequest(0, newFilters) Lo que consideraría una solución alternativa es almacenar la page como una referencia mutable de la siguiente manera:
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>; } o levántelo y páselo desde el exterior como accesorio (lo cual no es ideal, ya que quiero evitar filtrar esta dependencia al exterior). El problema con la solución anterior (usar useRef ) es que el componente no se volverá a procesar cuando se llame a nextPage y sería necesario forzar el reciclaje. El problema con este último es que deja fuera la 'carga' de administrar la dependencia de la page . Aunque lo soluciona. Mi pregunta es si existe una manera de estructurar el Fetcher para que obtengamos lo mejor de ambos mundos, es decir, el estado de la página internamente en el componente y que permanezca como parte del estado, en lugar de una referencia. Lo siento si este ejemplo parece un poco artificial, pero creo que este patrón ocurre con bastante frecuencia y estaría muy agradecido por cualquier aporte al respecto.
Puede resolver este problema eliminando los filters de la dependencia del segundo efecto.
¿Por qué? ya que se está asegurando de que la página activará una nueva representación para el efecto, por lo tanto, al configurar el filtro en ambos efectos, eso significa activar dos veces, una para el filtro y luego la página también se actualiza, por lo que esta segunda representación se activará.
Puedes consultar la demostración aquí .
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>; }Hay una nota importante: cada dependencia activará una nueva representación cuando se actualice, por lo que la colocamos dentro de la matriz de dependencias, pero también debe asegurarse del árbol de representación.
Además, tiene otra solución, guardando el filtro anterior y verificando el nuevo valor si es igual al anterior o no.
Además, puede usar el componente de reestructuración y la función de compilación y llamar a lo que necesita según las condiciones...
Y no recomiendo usar Ref en este caso... no es una solución para un estado normal, y creo que la primera solución es justa.
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>; } Además, setPage(() => () => 0); es una buena opción (como la respuesta de Pasato a continuación) para manejar este caso mediante la referencia de actualización cada vez que se activa una nueva representación.
Gracias Anees, eso fue muy simple. Hay una advertencia de borde que no se aborda en su solución y es el caso en el que los filtros cambian, mientras aún están en la página 0; en este caso, el efecto dependiente de la página no se activará. Finalmente opté por lo siguiente:
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>; } La pereza del estado es irrelevante en este caso: simplemente lo "envuelvo" en la función para que dos instancias page sean referencialmente diferentes, incluso si devuelven el mismo número. Además, el restablecimiento de la página no se ejecuta al montar, por lo que no consultamos dos veces.