#javascript #reactjs #typescript #react-hooks
Вопрос:
Я новичок в машинописи и даже реагирую на крючки. Я пытаюсь непрерывно извлекать данные из пользовательского вызываемого крючка useFetch()
, который принимает параметр string (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;
Я импортирую этот крючок в 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;
При первой загрузке приложения оно работает. Я получаю данные (цитату), когда нажимаю на кнопку.
Однако, когда я нажимаю на него несколько раз, API больше не вызывается, и новые данные не поступают. Я считаю, что я должен использовать useEffect()
крюк react и поддерживать состояние (возможно, я ошибаюсь), но все мои попытки до сих пор были безрезультатны. Любая помощь была бы очень признательна. Если я это выясню, я обязательно отвечу на этот вопрос. Спасибо!
Комментарии:
1. должен ли он перехватывать новую цитату с того же URL-адреса или звонить по другому URL-адресу при каждом щелчке?
2. @henk Это тот же URL-адрес.
Ответ №1:
Похоже, вы немного смешиваете здесь две идеи.
Вы хотите получать данные непрерывно? (Непрерывно, то есть все время, как в, периодически, с интервалом)
Или вы хотите получить данные при монтировании компонента (в настоящее время происходит) И когда пользователь нажимает кнопку «Получить котировку» (в настоящее время не работает)?
Я постараюсь помочь вам и с тем, и с другим.
Непрерывно / периодически
Попробуйте немного изменить настройку. Внутри useFetch сделайте что-нибудь вроде:
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;
Это будет извлекать конечную точку каждые 2 секунды и обновлять переменную состояния, затем это будет отражено в приложении (или в любом месте, где используется крючок, если на то пошло).
Поэтому вам больше не нужно состояние котировки в приложении. Кроме того, вам больше не нужна кнопка в пользовательском интерфейсе.
Приложение будет выглядеть примерно так:
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;
Получение данных при монтировании компонента (в настоящее время происходит) И когда пользователь нажимает кнопку «Получить цитату»
Тогда, на мой взгляд, крючок useFetch на самом деле не нужен/не подходит. Для этого приложения можно использовать крюк, но преобразование его в более простую функцию имело бы для меня больше смысла. Я предлагаю опустить крючок useFetch. Компонент приложения будет выглядеть примерно так:
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;
Ответ №2:
Проблема здесь в том, что вы не переставляете кавычки.
const useFetch = (url:string) => {
const [loading, setLoading] = useState(false);
const [quote, setQuote] = useState('');
const refetch = useCallback(() => {
setLoading(true);
fetch(url)
.then((x) => x.text())
.then((y) => {
//setQuote(y);
setQuote(String(Math.random()));
setLoading(false);
});
}, [url]);
return { quote, refetch, loading };
};
const App = () => {
const { quote, refetch, loading } = useFetch('/api/rand');
useEffect(() => {
refetch();
}, []);
return (
<div>
<h1>Quotes</h1>
<button onClick={refetch}>Get Quote</button>
{loading ? ' loading...' : ''}
<div>{quote}</div>
</div>
);
};
Смотрите этот пример
https://stackblitz.com/edit/react-ts-kdyptk?file=index.tsx
Ответ №3:
Вот еще одна возможность: я добавил функцию перезагрузки к возвращаемым значениям useFetch
. Кроме того, поскольку useFetch
крючок уже содержит свое собственное состояние, я удалил useState
из 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>
)
}
Ответ №4:
Просто измените код, как показано ниже:
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;