[英]React component not re-rendering on URL parameter change when using useEffect hook to fetch data
这是问题:
我有一个组件意味着≈ 25
不同的项目/页面具有相同的结构。 所以,当我尝试使用 React 时,我会做任何人都会做的事情,并且我将动态 URL 参数传递到我的 API 请求中(如下图所示)。
const [{ items, isLoading, isError }] = useDataApi(
`http://localhost:5000/api/teams/topspending/${params.team}`,
[],
params.team);
这只是使用为简单起见而分离的useEffect
组件(如下图所示)。
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;
任务很简单。 Upon clicking on a simple navigation link in the navbar to change the URL from http://localhost:5000/api/teams/topspending/Team1
to http://localhost:5000/api/teams/topspending/Team2
I am wishing that SAME 组件将在使用更新的 URL 获取新数据后重新渲染。
我已经尝试了很多事情......每次 URL 更改后,我都可以更新组件,但获取的数据仍然是旧数据!
(我正在使用 React-Router 将单个组件路由到此链接)
好的,我认为您的代码中有两个小问题。
在父 function
这是我的主要 function 将使用您的自定义挂钩。 如果您看到,我不使用插值,因为您的自定义钩子不会检测到它。 这就是为什么您的自定义挂钩中的initialUrl
变量(您的 URL)永远不会改变的原因。
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>
</>
);
};
自定义钩子内部
在我看来,您误解了 react 提供的setState function。 请记住,每次调用setState function 时都不是同步的。 我的意思是,如果您使用setUrl(initialUrl)
,那么在下一行代码中,您的 state 变量url
不一定已经更新了值。 要了解更多信息,您可以阅读以下内容: https://reactjs.org/docs/faq-state.html#when-is-setstate-asynchronous
我建议使用另一个变量来调用正确的 URL 并更改自定义挂钩的变量名称。 我在您的代码中添加了一些注释//Note:
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 }];
};
我希望,这可以帮助你..
setState 是异步的,因此无法保证在下一次渲染之前它何时会受到影响。 有多种方法可以重写代码以更可预测地工作,但是对于您提供的示例,最简单的解决方案是完全删除 url state 并在对initalUrl
的调用中使用axios
。
这不是很好。
因此,另一种选择是保留您的url
state 并添加第二个useEffect
观察url
。
例如。
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;
仍然不是很好,但你想做的事吗?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.