我正在尝试构建一个自定义的钩子,以处理异步调用的加载和错误行为。
我想这样使用它:
const { loading, error} = useFetch(fetchUser, id)
或
const {loading, error } = useFetch(updateUserName, id, name)
到目前为止,我有类似的东西:
function useFetch(fetchFn, ...params) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
useEffect(() => {
fetchFn(...params)
.then(() => {
setLoading(false);
})
.catch(() => {
setError(true);
});
}, [...params, fetchFn]);
return { loading, error };
}
我在useFetch(fetchFn, ...params)
上遇到了麻烦,因为在每个调用中都会创建一个新的params
数组。所以我不能在依赖项数组中使用它。但是我不能使用传播运算符。
是否有可能实现我想做的事情?
答案 0 :(得分:0)
大概您希望单个参数是依赖项,而不是这些参数的数组?因为在这种情况下,依赖关系实际上只是数组实例(正如您所说的,每次都会更改)。
不要在依赖项列表中散布参数,而是包括这些参数在依赖项列表中,即
useEffect(() => {
fetchFn(...params)
.then(() => {
setLoading(false);
})
.catch(() => {
setError(true);
});
}, [fetchFn].concat(params));
答案 1 :(得分:0)
我设法表现出想要的行为:
function useFetch(fetchFn, ...params) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
useEffect(() => {
fetchFn(...params)
.then(() => {
setLoading(false);
})
.catch(() => {
setError(true);
});
}, params);
return { loading, error };
}
我只是将params
作为依赖项数组传递。当然,eslint并不满意,但是我们可以直接禁用它。
我相信此实现是正确。
请随时分享您的观点,我对您的反馈意见非常感兴趣
答案 2 :(得分:-1)
这样做怎么样?
注意:这不是可运行的代码。它甚至没有被转译。它缺少部分代码,应该有很多错别字。仅供参考。
/*
Firstly, let's define a function to determine if two arrays have the same elements.
*/
const hasSameValues = (arr1 = [], arr2 = []) => {
if (arr1?.length !== arr2?.length) {
return false;
}
for (let i = 0, length = arr1?.length; i < length; i++) {
if (arr1[i] !== arr2[i]) {
return false;
}
}
return true;
}
/*
Now we define the compoenent that will receive the dependencies array
*/
const AircraftComboBox = ({source = () => {}, dependencies = [], onChange = () => {}}) => {
// Let's save the dependencies to evaluate if they've changed in the next render
const [storedDependencies, setStoredDependencies] = useState(dependencies);
const [availableAircrafts, setAvailableAircrafts] = useState([]);
useEffect(
() => {
// Use the hasSameValues function to determine in runtime if the array of dependecies has changed
setStoredDependencies(prev => hasSameValues(prev, dependencies) ? prev : dependencies)
},
[dependencies, setStoredDependencies] // this array of dependencies still hardcoded
);
useEffect(() => {
async function fetchData() {
const response = await source(); // Call the source function passed in props
setAvailableAircrafts([...response.data]);
}
fetchData();
}, [storedDependencies]); // this array of dependencies still hardcoded
return (
<>
<label for="aircrafts-combo">Aircraft</label>
<select id="aircrafts-combo" onChange={e => onChange(e)}>
{ availableAircrafts.map(option => <option value={option}>{option}</option>) }
</select>
</>
);
}
/**
Now we can use it.
Note that the Component using AircraftComboBox doesn't need to implement any useEffect.
*/
const AircraftFeature = () => {
const [category, setCategory] = useState('');
const [airCraft, setAircraft] = useState('');
return (
<>
<div>
<label for="category">Category</label>
<select id="category" type="text" onChange={e => setCategory(e.target.value)} >
<option value="ac">airplane</option>
<option value="rc">rotorcraft</option>
<option value="pl">powered lift</option>
<option value="gl">glider</option>
<option value="lta">lighter than air</option>
<option value="pp">powered parachute</option>
<option value="wsc">weight-shift-control</option>
</select>
</div>
{/**
Every time category's change, source function, defined in props, will be fired.
This will retrieve new data to populate the list of available options.
*/}
<AircraftComboBox
source={() => httpService.getAirplanesByCategory(category)}
dependencies={[category]}
onChange={e => setAircraft(e.target.value)}
/>
</>
);
}