我正在构建一个自定义钩子来管理来自 fetch/axios/promise/whateveryouwant 函数的数据,这个钩子允许我随时更新数据使用我在钩子末尾返回的更新函数。
所以现在我的钩子里有两种状态:
const [data, setData] = useState<T>(defaultValue); // the data coming from the fetch, T is defined by the user of the hook
const [query, setQuery] = useState<Array<{ key: string; value: string }>>([]); // array of query option for the URL
我想实现一种动态方式将查询字符串添加到 URL,因此我使用查询状态来拥有一个表示我想要的对象数组,但在从外部调用时无法访问更新函数内部的查询值。这是钩子,我强调了重要的部分:
export default function useFetchV2<T>(
defaultValue: T,
getData: (queryString?: string) => Promise<T>
): {
update: () => void;
data: T;
updateQueryValue: (key: string, value: string) => void; // update, data, updateQueryValue are the return value of the hook.
} {
const [data, setData] = useState<T>(defaultValue);
const [query, setQuery] = useState<Array<{ key: string; value: string }>>([]); // THE STATE I WANT TO ACCESS
console.log(query, data); // Here the state is updating well and everything seems fine
const update = useCallback((): void => {
let queryString;
console.log(query, data);
// But here is the problem, query and data are both at their default value. The state inside the hooks is correct but not here.
if (query.length > 0) { // query.length always at 0
queryString = _.reduce(
query,
(acc, el) => {
return `${acc}${el.key}=${el.value.toString()}`;
},
'?'
);
console.log(queryString);
}
getData(queryString).then(res => setData(res));
}, [data, getData, query]);
const updateQueryValue = useCallback(
(key: string, value: string): void => {
const index = query.findIndex(el => el.key === key);
if (index !== -1) {
if (!value) {
const toto = [...query];
_.pullAt(toto, index);
setQuery(toto);
} else {
setQuery(prev =>
prev.map(el => {
if (el.key === key) {
return { key, value };
}
return el;
})
);
}
} else {
console.log(key, value); // everything is logging well here, good key and value
setQuery([...query, { key, value }]); // setQuery correctly update the state
}
update();
},
[query, update]
);
useEffect(() => {
update();
}, []);
return { update, data, updateQueryValue };
}
这可能是我导出函数的方式,我仍然不习惯范围。
我从一个组件调用了updateQueryValue。调用函数,更改状态,但更新函数看不到差异。
代码调用setQuery
然后立即调用update
并期望query
更新值,但它将始终是query
的当前值。实现这一目标是useEffect
的用例。
} else {
console.log(key, value); // everything is logging well here, good key and value
setQuery([...query, { key, value }]); // setQuery correctly update the state
}
// This will *always* see the current value of `query`, not the one that
// was just created in the `else` block above.
update();
- 听起来代码应该在更新
query
后update
运行,这正是useEffect
的用途。
const updateQueryValue = useCallback(
(key: string, value: string): void => {
const index = query.findIndex(el => el.key === key);
if (index !== -1) {
if (!value) {
const toto = [...query];
_.pullAt(toto, index);
setQuery(toto);
} else {
setQuery(prev =>
prev.map(el => {
if (el.key === key) {
return { key, value };
}
return el;
})
);
}
} else {
setQuery([...query, { key, value }]); // setQuery correctly update the state
}
// This call isn't needed, it will always "see" the old value of
// `query`
// update();
},
[query, update]
);
// Call `update` once when this custom hook is first called as well as
// whenever `query` changes.
useEffect(() => {
update();
}, [query]); // <-- this is new, add `query` as a dependency
- 只有当某些东西依赖于函数的身份来做决策时,才需要
useCallback
;例如,如果一个组件将函数作为道具接收,它会检查道具是否更改以决定是否重新渲染。 在这种情况下,update
不需要useCallback
,并且可能会使此挂钩更难调试。
function update() {
let queryString;
if (query.length > 0) { // query.length always at 0
queryString = _.reduce(
query,
(acc, el) => {
return `${acc}${el.key}=${el.value.toString()}`;
},
'?'
);
console.log(queryString);
}
getData(queryString).then(res => setData(res));
});