dee*_* zg 9 reactjs react-hooks
如何发送带有响应钩的按钮单击上的HTTP请求?或者,就此而言,如何在单击按钮时产生任何副作用?
到目前为止,我看到的是“间接”的东西,例如:
export default = () => {
const [sendRequest, setSendRequest] = useState(false);
useEffect(() => {
if(sendRequest){
//send the request
setSendRequest(false);
}
},
[sendRequest]);
return (
<input type="button" disabled={sendRequest} onClick={() => setSendRequest(true)}
);
}
Run Code Online (Sandbox Code Playgroud)
那是正确的方式还是还有其他模式?
Shu*_*tri 21
您不需要效果来发送按钮点击请求,而是您需要的只是一个处理程序方法,您可以使用useCallback方法进行优化
const App = (props) => {
//define you app state here
const fetchRequest = useCallback(() => {
// Api request here
}, [add dependent variables here]);
return (
<input type="button" disabled={sendRequest} onClick={fetchRequest}
);
}
Run Code Online (Sandbox Code Playgroud)
使用变量 with 跟踪请求useEffect不是一个正确的模式,因为您可以使用 useEffect 设置状态来调用 api,但是由于其他一些更改而导致的额外渲染将导致请求进入循环
Far*_*uti 11
在函数式编程中,任何异步函数都应被视为副作用。
在处理副作用时,您需要将启动副作用的逻辑和该副作用结果的逻辑分开(类似于 redux saga)。
基本上,按钮的职责只是触发副作用,而副作用的职责是更新 dom。
此外,由于 React 正在处理组件,因此您需要确保组件在任何之前setState或之后仍然安装await,这取决于您自己的喜好。
为了解决这个问题,我们可以创建一个自定义钩子,useIsMounted这个钩子可以让我们轻松检查组件是否仍然挂载
/**
* check if the component still mounted
*/
export const useIsMounted = () => {
const mountedRef = useRef(false);
const isMounted = useCallback(() => mountedRef.current, []);
useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
};
});
return isMounted;
};
Run Code Online (Sandbox Code Playgroud)
那么你的代码应该是这样的
export const MyComponent = ()=> {
const isMounted = useIsMounted();
const [isDoMyAsyncThing, setIsDoMyAsyncThing] = useState(false);
// do my async thing
const doMyAsyncThing = useCallback(async () => {
// do my stuff
},[])
/**
* do my async thing effect
*/
useEffect(() => {
if (isDoMyAsyncThing) {
const effect = async () => {
await doMyAsyncThing();
if (!isMounted()) return;
setIsDoMyAsyncThing(false);
};
effect();
}
}, [isDoMyAsyncThing, isMounted, doMyAsyncThing]);
return (
<div>
<button disabled={isDoMyAsyncThing} onClick={()=> setIsDoMyAsyncThing(true)}>
Do My Thing {isDoMyAsyncThing && "Loading..."}
</button>;
</div>
)
}
Run Code Online (Sandbox Code Playgroud)
注意:最好将副作用的逻辑与触发效果的逻辑分开(useEffect)
更新:
与上述所有复杂性不同,只需使用useAsync库useAsyncFn中的react-use内容,它更加干净和直接。
例子:
/**
* check if the component still mounted
*/
export const useIsMounted = () => {
const mountedRef = useRef(false);
const isMounted = useCallback(() => mountedRef.current, []);
useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
};
});
return isMounted;
};
Run Code Online (Sandbox Code Playgroud)
DoX*_*icK 10
export default () => {
const [isSending, setIsSending] = useState(false)
const sendRequest = useCallback(async () => {
// don't send again while we are sending
if (isSending) return
// update state
setIsSending(true)
// send the actual request
await API.sendRequest()
// once the request is sent, update state again
setIsSending(false)
}, [isSending]) // update the callback if the state changes
return (
<input type="button" disabled={isSending} onClick={sendRequest} />
)
}
Run Code Online (Sandbox Code Playgroud)
这就是您要发送单击请求并在发送过程中禁用按钮时将其归结为
@tkd_aj指出这可能会发出警告:“无法在已卸载的组件上执行React状态更新。这是空操作,但表明您的应用程序内存泄漏。要修复,取消所有订阅并进行异步useEffect清理功能中的任务”。
实际上,发生的情况是请求仍在处理中,与此同时,您的组件也已卸载。然后,它尝试setIsSending在未安装的组件上使用(setState)。
export default () => {
const [isSending, setIsSending] = useState(false)
const isMounted = useRef(true)
// set isMounted to false when we unmount the component
useEffect(() => {
return () => {
isMounted.current = false
}
}, [])
const sendRequest = useCallback(async () => {
// don't send again while we are sending
if (isSending) return
// update state
setIsSending(true)
// send the actual request
await API.sendRequest()
// once the request is sent, update state again
if (isMounted.current) // only update if we are still mounted
setIsSending(false)
}, [isSending]) // update the callback if the state changes
return (
<input type="button" disabled={isSending} onClick={sendRequest} />
)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8496 次 |
| 最近记录: |