我有一个名为useFetchMyApi
的自定义钩子,它包装了对API端点的提取调用。函数钩子接受一个参数,它包含在post主体中。数据数组输出取决于钩子参数。
在UI上,App只调用一次useFetchMyApi
。单击处理程序将调用后续的useFetchMyApi
来更新UI上的列表。
我的问题:
我不能在事件处理程序中调用useFetchMyApi
,因为它违反了钩子的规则。
import React, { useState, useEffect, useRef } from "react";
import "./styles.css";
const useFetchMyApi = (location = "") => {
const [items, setItems] = useState([]);
useEffect(() => {
// let's stimulate the API call with a 2 seconds response time
setTimeout(() => setItems([location, Math.random()]), 5000);
}, [location]);
return { items };
};
export default function App() {
const locationSelect = useRef(null);
const { items } = useFetchMyApi("mylocation1");
const onButtonClicked = (event) => {
const selectedLocation = locationSelect.current.value;
// Call api and refresh items by location
// Fix here??
//useFetchMyApi(selectedLocation);
};
return (
<div className="App">
<select ref={locationSelect}>
<option value="location1">Location 1</option>
<option value="location2">Location 2</option>
</select>
<button onClick={onButtonClicked}>Refresh</button>
<ul>
{items.map((item) => (
<li>{item}</li>
))}
</ul>
</div>
);
}
https://codesandbox.io/s/stupefied-water-1o6mz?file=/src/App.js:0-1055
发布于 2020-12-28 04:40:12
问题
useFetchMyApi
,因为它违反了钩子的规则.正确的,反应钩只能从主体的功能部件和其他反应钩调用。不能有条件地调用它们,例如在异步回调或循环中。
如何刷新依赖于useFetchMyApi
响应的项列表?
在App
中没有任何东西可以触发重发器来触发效果,从而触发效果的回调。
解决方案
您应该利用自定义useEffect
钩子中的useFetchMyApi
钩子的依赖性来刷新项目列表。添加一些本地组件状态来表示“当前”选定的位置。使用您的按钮的onClick
回调来更新状态并触发一个重命名器,然后重新运行您的自定义钩子。
const useFetchMyApi = (location = "") => {
const [items, setItems] = useState([]);
useEffect(() => {
// let's stimulate the API call with a 2 seconds response time
setTimeout(() => setItems([location, Math.random()]), 2000);
}, [location]); // <-- effect callback triggers when location updates
return { items };
};
function App() {
const locationSelect = useRef(null);
const [location, setLocation] = useState("mylocation1"); // <-- add state
const { items } = useFetchMyApi(location); // <-- pass location value to hook
const onButtonClicked = () => {
const selectedLocation = locationSelect.current.value;
setLocation(selectedLocation); // <-- update state, trigger rerender
};
return (
<div className="App">
<select ref={locationSelect}>
<option value="location1">Location 1</option>
<option value="location2">Location 2</option>
</select>
<button onClick={onButtonClicked}>Refresh</button>
<ul>
{items.map((item) => (
<li>{item}</li>
))}
</ul>
</div>
);
}
替代解
另一种解决方案是重写自定义的useFetchMyApi
钩子,以返回要在单击处理程序中按需调用的回调,以执行获取和更新列表的操作。这里有一个简单的转换。注意,现在返回的fetch函数使用的是位置,而不是钩子调用。
const useFetchMyApi = () => {
const [items, setItems] = useState([]);
// custom fetch function consumes location
const locationFetch = (location = "") => {
// let's stimulate the API call with a 2 seconds response time
setTimeout(() => setItems([location, Math.random()]), 2000);
};
return [items, locationFetch]; // <-- return state and fetch function
};
export default function App() {
const locationSelect = useRef(null);
const [items, locationFetch] = useFetchMyApi(); // <-- destructure state and fetch function
useEffect(() => {
locationFetch("mylocation1");
}, []); // <-- initial mounting fetch call
const onButtonClicked = () => {
const selectedLocation = locationSelect.current.value;
locationFetch(selectedLocation); // <-- invoke fetch function
};
return (
<div className="App">
<select ref={locationSelect}>
<option value="location1">Location 1</option>
<option value="location2">Location 2</option>
</select>
<button onClick={onButtonClicked}>Refresh</button>
<ul>
{items.map((item) => (
<li>{item}</li>
))}
</ul>
</div>
);
}
https://stackoverflow.com/questions/65472666
复制相似问题