如何在单击React Hooks方式上发送请求?

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

更新:

与上述所有复杂性不同,只需使用useAsyncuseAsyncFn中的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)

  • 你的意思是 useCallback 而不是 useEffect ?嗯,因为它是回调而不是副作用 (2认同)