Soy nuevo en TypeScript e incluso en React Hooks. Estoy tratando de obtener datos continuamente de un gancho personalizado llamado useFetch() , que toma un parámetro de cadena (la URL):
import { useEffect, useState } from "react"; export interface Payload { data: null | string, loading: boolean } const useFetch = (url: string) => { const [state, setState] = useState<Payload>({data: null, loading: true}) useEffect(() => { setState(state => ({ data: state.data, loading: true })); fetch(url) .then(x => x.text()) .then(y => { setState({ data: y, loading: false }); }); }, [url, setState]) return state; } export default useFetch; Importo ese gancho en App() :
import React, { useState, useEffect } from "react"; import useFetch from './utils/useFetch' import {Payload} from './utils/useFetch'; const App = () => { const [quote, setquote] = useState<Payload>({data: null, loading: true}) const handleClick = () => setquote(data) const data = useFetch( "/api/rand" ); return ( <div> <h1>Quotes</h1> <button onClick={handleClick}>Get Quote</button> <div>{quote.data}</div> </div> ) } export default App;La primera vez que carga la aplicación, funciona. Obtengo datos (una cotización) cuando hago clic en el botón.
Sin embargo, cuando hago clic en él varias veces, no se vuelve a llamar a la API y no aparecen nuevos datos. Creo que se supone que debo usar el gancho de reacción useEffect() y mantener el estado (tal vez me equivoque), pero todos mis intentos hasta ahora han sido en vano. Cualquier ayuda sería muy apreciada. Si lo descubro, definitivamente responderé esta pregunta. ¡Gracias!
Parece que estás mezclando dos ideas un poco aquí.
¿Quieres obtener datos de forma continua? (Continuamente significa todo el tiempo, como en, periódicamente, en un intervalo)
¿O desea obtener datos sobre el montaje del componente (actualmente sucede) Y cuando el usuario toca el botón "Obtener cotización" (no funciona actualmente)?
Intentaré ayudarte con las dos.
Continuamente/periódicamente
Prueba a cambiar un poco la configuración. Dentro de useFetch haz algo como:
import { useEffect, useState } from "react"; export interface Payload { data: null | string, loading: boolean } const useFetch = (url: string) => { const [state, setState] = useState<Payload>({data: null, loading: true}) useEffect(() => { const interval = setInterval(() => { setState(state => ({ data: state.data, loading: true })); fetch(url) .then(x => x.text()) .then(y => { setState({ data: y, loading: false }); }); }, 2000); // Something like 2s return () => { clearInterval(interval); // clear the interval when component unmounts } }, [url]) return state; } export default useFetch;Esto buscará el punto final cada 2 segundos y actualizará la variable de estado, esto luego se reflejará en la aplicación (o en cualquier lugar que use el enlace).
Por lo tanto, ya no necesita el estado de cotización en la aplicación. Además, ya no necesita un botón en la interfaz de usuario.
La aplicación se verá algo como:
import React, { useState, useEffect } from "react"; import useFetch from './utils/useFetch' import {Payload} from './utils/useFetch'; const App = () => { const quote = useFetch( "/api/rand" ); return ( <div> <h1>Quotes</h1> <div>{quote.data}</div> </div> ) } export default App;Obtener datos al montar el componente (actualmente sucede) Y cuando el usuario presiona el botón "Obtener cotización"
Entonces, en mi opinión, el gancho useFetch no es realmente necesario/adecuado. Se puede usar un enlace para esta aplicación, pero transformarlo en una función más simple tendría más sentido para mí. Sugiero omitir el gancho useFetch. El componente de la aplicación se vería así:
import React, { useState, useEffect, useCallback } from "react"; const App = () => { const [quote, setQuote] = useState<Payload>({data: null, loading: true}) const handleGetQuote = useCallback(() => { setQuote(state => ({ data: state.data, loading: true })); fetch("/api/rand") .then(x => x.text()) .then(y => { setQuote({ data: y, loading: false }); }); }, []); useEffect(() => { handleGetQuote(); }, [handleGetQuote]); return ( <div> <h1>Quotes</h1> <button onClick={handleGetQuote}>Get Quote</button> <div>{quote.data}</div> </div> ) } export default App;Aquí hay otra posibilidad: agregué una función de recarga a los valores de retorno de useFetch . Además, dado que el useFetch ya contiene su propio estado, useState de la App
const useFetch = (url: string) => { const [state, setState] = useState<Payload>({data: null, loading: true}) const [reloadFlag, setReloadFlag] = useState(0) useEffect(() => { if(reloadFlag !== 0){ // remove this if you want the hook to fetch data initially, not just after reload has been clicked setState(state => ({ data: state.data, loading: true })); fetch(url) .then(x => x.text()) .then(y => { setState({ data: y, loading: false }); }); } }, [url, setState, reloadFlag]) return [state, ()=>setReloadFlag((curFlag)=>curFlag + 1)]; } const App = () => { const [data, reload] = useFetch( "/api/rand" ); return ( <div> <h1>Quotes</h1> <button onClick={reload}>Get Quote</button> <div>{quote.data}</div> </div> ) }Simplemente cambie el código de la siguiente manera:
const useFetch = (url: string) => { const [state, setState] = useState<Payload>({data: null, loading: true}) setState(state => ({ data: state.data, loading: true })); fetch(url) .then(x => x.text()) .then(y => { setState({ data: y, loading: false }); }); return state; } export default useFetch;