我正在使用钩子在我的应用程序中获取数据。钩子看起来像这样:
const initialState = {
response: null,
loading: true,
error: null
}
const useGetFetch(url, token, user, parser) => {
const [state, dispatch] = useReducer(fetchReducer, initialState)
useEffect (() => {
if(user){
fetch(...)
.then((data)=> data.json())
.then(dispatch(...)) // setting the state here
.catch((error) => dispatch(...)) // set error state
}
}, [user])
return [state.response, state.loading, state.error]
}
具体来说,我在表单组件中使用此钩子来获取一些数据以用作选择元素中的选项。
但是,我需要能够让用户即时添加新选项。为了方便这一点,我在选择元素旁边创建了一个按钮。如果用户按下此按钮,将弹出一个带有另一个表单的模态,从而可以创建新选项。这个新选项通过 post 请求发送到服务器,如果成功,模式将关闭。
我面临的问题是,在模式关闭后,除非我刷新页面,否则我无法更新使用自定义数据获取钩子获取的初始数据。有没有办法在不触发页面刷新的情况下以某种方式实现这一目标?
我最初的预感是使用回调函数并将其传递给useGetFetch
并将其绑定到useEffect
钩子。然后我可以将其传递给模态中的表单,并在成功提交后调用该函数,但这不起作用。
我正在寻找的另一个选择是useCallback
钩子,但我目前还不够了解它,无法知道它是否有帮助。
编辑
具体来说,我所做的是在父组件之外创建一个触发器函数:
const trigger = () => console.log("click")
将其绑定到获取钩子:
const useGetFetch(url, token, user, parser, trigger) => {
const [state, dispatch] = useReducer(fetchReducer, initialState)
useEffect (() => {
if(user){
fetch(...)
.then((data)=> data.json())
.then(dispatch(...)) // setting the state here
.catch((error) => dispatch(...)) // set error state
}
}, [user, trigger])
return [state.response, state.loading, state.error]
}
并将其传递给父组件内的子组件:
const trigger = () => console.log("click")
const Parent = () => {
// load a bunch of stuff
// ...
const { show, toggle } = useModal()
const [optionsData, Loading, Error] = useGetFetch(
optionsUrl,
token,
user,
parser,
trigger
)
// ...
return (
<div>
{// ...}
<button
onClick={(e) => {
e.preventDefault()
toggle()
}}
>
Add
</button>
<Modal show={show} toggle={toggle}>
<ModalForm
show={show}
toggle={toggle}
trigger={trigger}
></ModalForm>
</Modal>
{// ...}
<div>
)
}
在内部ModalForm
触发器在执行成功的 post 请求后调用。关闭模态后,数据不会更新。我的猜测是,ModalForm
内部的调用触发器会触发useGetFetch
内部的useEffect
,但它似乎不是那样工作的。
如果清楚地理解您的问题,我认为您要做的是在用户关闭模态时强制重新获取。
您可以通过在useGetFetch
钩子内触发强制重新渲染来做到这一点,如下所示:
const useGetFetch(url, token, user, parser) => {
const [state, dispatch] = useReducer(fetchReducer, initialState)
// calling refetch will force a rerender on the hook
const [shouldRefetch, refetch] = useState({});
useEffect (() => {
if(user){
fetch(...)
.then((data)=> data.json())
.then(dispatch(...)) // setting the state here
.catch((error) => dispatch(...)) // set error state
}
}, [user, shouldRefetch]); // note the dep array
// returning the refetch functions so we can call the refetch() on modal close.
return [state.response, state.loading, state.error, refetch];
}
这就是useGetFetch,现在您将像这样使用钩子:-
const Parent = () => {
const { show, toggle } = useModal();
// notice the refetch method
const [optionsData, Loading, Error, refetch] = useGetFetch(
optionsUrl,
token,
user,
parser
);
return (
<div>
<button
onClick={e => {
e.preventDefault();
toggle();
}}
>
Add
</button>
<Modal show={show} toggle={toggle}>
<ModalForm
show={show}
toggle={toggle}
trigger={() => {
// probably onClose would be better name instead of `trigger`
// calling the refetch method with empty object, this will cause rerender
refetch({});
}}
></ModalForm>
</Modal>
</div>
);
};
我认为您在useEffect函数中缺少,
这就是为什么您得到X不是一个函数错误的原因
useEffect (() => {
if(user){
fetch(...)
.then((data)=> data.json())
.then(dispatch(...)) // setting the state here
.catch((error) => dispatch(...)) // set error state
}
//comma here
},[user]);
我已经使用布尔值实现了一个解决方案,但认为一定有更好的方法来做到这一点。使用对象的公认解决方案是相似的,但我不喜欢公开的 API 或它的外观refresh({})
或refresh(!resfreshSomething)
。
因此,我想出了以下解决方案:
const useGetFetch(url, token, user, parser) => {
const [state, dispatch] = useReducer(fetchReducer, initialState)
const [shouldRefetch, refetch] = useState({});
const refresh = () => refetch({});
// call upon useEffect (() => { ...
return [state.response, state.loading, state.error, refresh];
}
这样,您就可以简单地在钩子外使用refresh();
。