Tengo una función que obtiene datos.
const fetchData = async (filter) => { if (loading) return loading = true const data = await api(filter) setData(data) loading = false } También tengo un componente de filtro, cuando cambio los filtros llama a mi función fetchData() con la nueva variable de filtro.
Todo esto funciona, sin embargo, hay un problema.
Este problema ocurre cuando cambio mi filtro pero mi función de obtención está en estado de carga. Esto hace que la verificación if falle y ahora veo datos obsoletos porque nunca ocurre una nueva recuperación.
Mi idea inicial era crear una variable const q = [] , y dentro if (loading) empujaría mis filtros, y de alguna manera al final volvería a buscar con el último elemento dentro de mi matriz q y luego borraría esa matriz.
Realmente no sé cómo hacer esa lógica de recuperación. Un setInterval(() => checkQ(), 1000) ? no parece correcto
¿Cuál sería un mejor enfoque a seguir?
Debe usar un AbortController ; eso es parte de la fetch , ya que mi experiencia me dice que no es difícil iniciar una nueva solicitud de fetch , pero ¿qué hacer con la primera solicitud cuando envía una segunda?
Aquí hay un fragmento que hará lo que pediste, pero también se ocupa de las solicitudes innecesarias:
const { useState, useEffect } = React const useFetchData = () => { const [users, setUsers] = useState([]) let controller = null const fetchData = () => { console.log('fetch initiated') if (controller) controller.abort() controller = new AbortController(); const { signal } = controller; fetch('https://jsonplaceholder.typicode.com/users', { signal }) .then(response => { console.log('request response') return response.json() }) .then(json => { console.log('retrieved list:', json) setUsers(() => json || []) }) .catch(err => { if(err.name === "AbortError") { console.warn('Abort error', err) } }) } return { fetchData } } const FetchData = () => { const { fetchData } = useFetchData() return ( <div> <button onClick={fetchData}>FETCH DATA</button><br /> </div> ) } const FetchAbortFetchData = () => { const { fetchData } = useFetchData() return ( <div> <button onClick={() => { fetchData() fetchData() }}>FETCH-ABORT-FETCH DATA</button><br /> </div> ) } const App = () => { return ( <div> <FetchData /><br /> <FetchAbortFetchData /> </div> ) } ReactDOM.render(<App />, document.getElementById('root')) <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script> <div id="root"></div>La forma más fácil es usar sus criterios de filtro como bloqueo.
Ventajas
Desventajas
let latestFilter = null; const fetchData = async (filter) => { // Check to see if its there's the same request already in progress // Note: may need to deep equal to check the filter if (filter === latestFilter) return; // Set the current request as most up to date latestFilter = filter // Fetch Data (async) const data = await api(filter) // If the filter is still the most up to date, use it. Otherwise discard. // Note: may need to deep equal to check the filter if (filter === latestFilter) { setData(data) latestFilter = null } }Para resolver la desventaja 2, puede incluir un contador. Esto asegurará que solo la solicitud más reciente ejecute setData.
let latestFilter = null; let latestRequestNumber = 0 const fetchData = async (filter) => { if (filter === latestFilter) return; latestFilter = filter // Identify the current request const requestNumber = latestRequestNumber + 1; // Store this number latestRequestNumber = requestNumber const data = await api(filter) // Update data only if we are the most recent request. if (callCount = latestCallCount) { setData(data) latestFilter = null } }