javascript: el componente React no se vuelve a representar al cambiar el parámetro de URL cuando se usa el gancho useEffect para

CorePress2024-01-24  10

Aquí está el problema:

Tengo un componente que debe tener la misma estructura para ≈ 25 elementos/páginas diferentes. Entonces, hago lo que cualquiera haría al intentar usar React, y paso parámetros de URL dinámicos a mi solicitud de API (que se muestra a continuación).

const [{ items, isLoading, isError }] = useDataApi(
`http://localhost:5000/api/teams/topspending/${params.team}`,
[],
params.team);

Esto es simplemente usar un componente useEffect que se ha separado para simplificar (que se muestra a continuación).

const useDataApi = (initialUrl, initialData, effect) => {
  console.log("start/top Data API");
  const [items, setItems] = useState(initialData);
  const [url, setUrl] = useState(initialUrl);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {

    setUrl(initialUrl);

    const abortCtrl = new AbortController();
    const opts = { signal: abortCtrl.signal };

    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
      console.log("data loading");

      try {
        console.log(url, "this should be the new Url");
        const result = await axios(url, opts);

        setItems(result.data.data);
      } catch (error) {
        setIsError(true);
      }
      console.log("data loaded...");
      setIsLoading(false);
    };

    fetchData();
    return () => abortCtrl.abort();
  }, [effect]);

  return [{ items, isLoading, isError }];
};

export default useDataApi;

La tarea es bastante sencilla. Al hacer clic en un enlace de navegación simple en la barra de navegación para cambiar la URL de http://localhost:5000/api/teams/topspending/Team1 a http://localhost:5000/api/teams/topspending/Team2, deseo que el MISMO componente se volverá a representar después de obtener datos NUEVOS con la URL ACTUALIZADA.

He probado muchas cosas... y puedo hacer que el componente se actualice después de cada cambio de URL.¡PERO los datos obtenidos siguen siendo los datos ANTIGUOS!

(Estoy usando React-Router para enrutar el componente único a este enlace)

El gancho useDataApi está incompleto. ¿Podría agregar los argumentos necesarios? Es decir, falta la parte superior de la función.

-yudhiesh

27 de marzo de 2021 a las 5:22

¿Son correctos los nuevos datos de la URL dinámica? Si es así, ¿puede obtener datos nuevos, actualizar el estado y el componente no se vuelve a renderizar? Además, el estado de la URL no es necesario, solo úselo para los axios sin volver a configurar el estado sin ningún motivo

-Michael Parkadze

27 de marzo de 2021 a las 5:29

@yudhiesh Lo siento, acabo de actualizar el fragmento de código para mostrar los parámetros de useDataApi. Tiene 3 parámetros que paso para estado, errores y actualización de efecto de uso.

- DOZBORNE

27 de marzo de 2021 a las 5:30

@MichaelParkadze Gracias por la respuesta. Acabo de actualizar algunos parámetros que faltaban en useDataApi en el fragmento de código anterior. Los nuevos datos de la URL son correctos, pero el gancho useEffect no los ve hasta que uso setUrl dentro de la función. PERO, por alguna razón, no importa dónde dentro de la función coloque setUrl, useEffect solo actualiza la URL DESPUÉS de haber iniciado la búsqueda de datos nuevamente (con la URL anterior). Si no agrego setUrl dentro de useEffect, parece que no puedo elegir useEffectal día con el cambio. ¿Tiene eso sentido?

- DOZBORNE

27 de marzo de 2021 a las 5:36



------------------------------------

Vale, creo que hay dos pequeños problemas en tu código.

Dentro de la función principal

Esta es mi función principal que utilizará su gancho personalizado. Si ve, no uso la interpolación porque su gancho personalizado no la detectará. Es por eso que su variable initialUrl (Su URL) en su enlace personalizado nunca cambia.

const App = () => {
  const [id, setId] = React.useState(1);
  const response = useDataApi(
    'https://jsonplaceholder.typicode.com/posts/' + id,
    [],
    id,
  );

  return (
    <>
      <div>My id {id}</div>
      <button onClick={() => setId(id + 1)}>Click Me!</button>
    </>
  );
};

Dentro del gancho personalizado

Me parece que estás malinterpretando la función setStateproporcionado por reaccionar. Recuerde que cada vez que llama a la función setState no es sincrónica. Quiero decir, si usa setUrl(initialUrl), en la siguiente línea de código la URL de su variable de estado no necesariamente tendrá los valores ya actualizados. Para saber más al respecto, puedes leer esto: https://reactjs.org/docs/faq-state.html#when-is-setstate-asynchronous

Sugeriría usar otra variable para llamar a la URL correcta y cambiar los nombres de las variables de su enlace personalizado. Agregué algunos comentarios a tu código //Nota:

export const useDataApi = (initialUrl, initialData, effect) => {
  console.log("start/top Data API", effect, initialUrl);
  const [items, setItems] = useState(initialData);
  const [url, setUrl] = useState(initialUrl);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    // Note: This not sync function
    setUrl(initialUrl);

    const abortCtrl = new AbortController();
    const opts = { signal: abortCtrl.signal };

    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
      console.log("data loading");

      try {
        console.log(url, "this should be the new Url");
        // Note: I changed this url variable of your state, to use your initialUrl variable. (this initialUrl parameter should have your UPDATED URL)
        const result = await axios(initialUrl, opts);

        setItems(result.data);
      } catch (error) {
        setIsError(true);
      }
      console.log("data loaded...");
      setIsLoading(false);
    };

    fetchData();
    return () => abortCtrl.abort();
  }, [effect]);

  return [{ items, isLoading, isError }];
};

¡Espero que esto pueda ayudarte!.

1

¡Ya veo! Todavía estoy un poco atrasado en comprender useState... Pensé que lo sabía, ¡pero claramente no! Terminé simplemente sacando setUrl dentro de useEffect porque creo que era inútil. Para que quede claro, me recomiendas mantener el estado de uso de la URL y simplemente cambiar el nombre de URL inicial a algo más práctico, ¿correcto? ¡Muchas gracias de nuevo! ¡Espero haberlo estructurado al menos correctamente! ¡Gracias por tu ayuda!

- DOZBORNE

27 de marzo de 2021 a las 6:41



------------------------------------

setState es asíncrono, por lo que no hay garantía de cuándo se verá afectado antes del siguiente renderizado. hayHay varias formas de reescribir su código para que funcione de manera más predecible, pero con los ejemplos que ha proporcionado, la solución más sencilla es eliminar el estado de la URL por completo y simplemente usar initalUrl en su llamada a axios.

Esto no es genial.

Entonces, otra opción sería mantener el estado de tu URL y agregar un segundo useEffect que vigile la URL.

p.ej.

const useDataApi = (initialUrl, initialData, effect) => {
  console.log("start/top Data API");
  const [items, setItems] = useState(initialData);
  const [url, setUrl] = useState(initialUrl);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    setUrl(initialUrl);
  }, [effect]);

  useEffect(() => {
    const abortCtrl = new AbortController();
    const opts = { signal: abortCtrl.signal };

    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
      console.log("data loading");

      try {
        console.log(url, "this should be the new Url");
        const result = await axios(url, opts);

        setItems(result.data.data);
      } catch (error) {
        setIsError(true);
      }
      console.log("data loaded...");
      setIsLoading(false);
    };

    fetchData();
    return () => abortCtrl.abort();

  }, [url])

  return [{ items, isLoading, isError }];
};

export default useDataApi;

Aún no es genial, pero ¿lo que intentas hacer?

Compartir mejorar esta respuesta SeguirRespondido

27 de marzo de 2021 a las 5:53

usuario3282374

usuario3282374

3

¡Esto funcionó! Anteriormente, intenté usar otro useEffect en mi componente principal, pero no pude hacerlo funcionar por muchas razones. Veo cómo funciona, pero supongo que no sé por qué. Todavía soy bastante nuevo en React, pero no estoy seguro de por qué no detecta la NUEVA URL inicial cuando cambia. Además, si coloco un setUrl dentro del useEffect original, tampoco lo detectaría, no estoy seguro de por qué.¿sin embargo? ¡Esto es genial! ¡Realmente lo aprecio! ¿Pero dijiste que había una mejor manera de escribir el código en general? sugerencias? ¡Probablemente quiero duplicar este proceso muchas veces!

- DOZBORNE

27 de marzo de 2021 a las 6:07

Parece que desea utilizar el parámetro de efecto como activador para llamar a la API, pero también confía en que la URL pueda cambiar, lo que probablemente también debería activar la llamada a la API. Si es así, mi primera sugerencia es mejor y deberías cambiar tu configuración inicial.de para eliminar el enlace de estado para la URL y hacer que su efecto único observe tanto el efecto como la URL. Además, ¿también podría ser mejor eliminar el parámetro de efecto por completo y simplemente observar si hay un cambio en la URL?

usuario3282374

27 de marzo de 2021 a las 6:20

¡Entendido! ¡Eso tiene mucho sentido ahora! ¡Gracias por su ayuda! ¡Realmente significa mucho!

- DOZBORNE

27 de marzo de 2021 a las 6:42

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare