实现 useFetch 反应钩子以在提交功能内工作

gol*_*cks 3 reactjs react-hooks

我有很多反应经验,但我是钩子的新手。
useFetch在这个useAsync钩子之后修改了以下钩子:

import { useState, useEffect, useCallback } from 'react'

export default function useFetch(url, options, { immediate }) {
  const [data, setData] = useState(null)
  const [error, setError] = useState(null)
  const [isPending, setIsPending] = useState(false)

  const executeFetch = useCallback(async () => {
    setIsPending(true)
    setData(null)
    setError(null)
    await fetch(url, options)
      .then((response) => response.json())
      .then((response) => setData(response))
      .catch((err) => setError(err))
      .finally(() => setIsPending(false))
    return { data, error, isPending }
  }, [url, options, data, error, isPending])

  useEffect(() => {
    if (immediate) {
      executeFetch()
    }
  }, [executeFetch, immediate])
  return { data, error, isPending, executeFetch }
}

Run Code Online (Sandbox Code Playgroud)

我的问题是我想在提交函数中使用它,而钩子在其他函数中不起作用,就像这样(为简洁起见,代码的简化版本):

export default function SignupModal({ closeModal }) {
  const { executeFetch } = useFetch(url, {options},
    { immediate: false }
  )

  async function handleSubmit(evt) {
    evt.preventDefault()
    const { data, error, isPending } = await executeFetch()
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

目前我故意在调用中抛出错误,但错误变量仍然是null.

我在这里缺少什么?这甚至可以用钩子实现吗?

提前致谢!

Mat*_*adi 6

React hook 只能用在你的组件体中,不能用在另一个函数中。executeFetch本身正在返回{ data, error, isPending },这使它成为一个嵌套的钩子,所以你不能在你的 handleSubmit 中使用它。

useFetch已经返回,{ data, error, isPending, executeFetch }因此 executeFetch 不需要再次返回。您可以从 useFetch 挂钩访问所有这些数据。当您在组件中调用 executeFetch 数据时,数据、错误和 isPending 将被更新,setState这将导致您的钩子为这些更新的任何值返回一组新值。

export default function useFetch(url, options, { immediate }) {
  const [data, setData] = useState(null)
  const [error, setError] = useState(null)
  const [isPending, setIsPending] = useState(false)

  const executeFetch = useCallback(async () => {
    setIsPending(true)
    setData(null)
    setError(null)
    await fetch(url, options)
      .then((response) => response.json())
      .then((response) => setData(response))
      .catch((err) => setError(err))
      .finally(() => setIsPending(false))
  }, [url, options, data, error, isPending])

  useEffect(() => {
    if (immediate) {
      executeFetch()
    }
  }, [executeFetch, immediate])
  return { data, error, isPending, executeFetch }
}
Run Code Online (Sandbox Code Playgroud)
export default function SignupModal({ closeModal }) {
  const { executeFetch, data, error, isPending } = useFetch(url, {options},
    { immediate: false }
  )

  async function handleSubmit(evt) {
    evt.preventDefault()
    await executeFetch()
  }    
  ...
  // Example in your return function
  {error != null && <Error />}
  <Button state={isPending ? 'processing' : 'normal'}

}
Run Code Online (Sandbox Code Playgroud)

根据评论更新

如果您需要访问 handleSubmit 函数中的数据或错误,则需要在钩子中返回承诺的响应/错误,这样您也应该能够访问 handleSubmit 中的数据/错误。

此外,我建议在用户触发 handleSubmit 之前将可能会更改的选项或任何其他变量数据作为参数传递给 executeFetch,以便 executeFetch 始终可以获取最新数据。

CodeSandBox 示例 1

CodeSandBox 示例 2

const useFetch = url => {
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);
  const [data, setData] = useState(null);

  const executeFetch = useCallback(
    // Here you will access to the latest updated options. 
    async ({ options }) => {
      setIsPending(true);
      setError(null);
        return await fetch(url, options)
        .then(response => response.json())
        .then(response => {
          setData(response); 
          return response;
        })
        .catch(err => {
          setError(err.message)
          return err;
        })
        .finally(() => setIsPending(false));
    },
    [url, setIsPending, setError]
  );
  return { data, error, isPending, executeFetch }
};
Run Code Online (Sandbox Code Playgroud)
const { data, executeFetch, error, isPending } = useFetch("URL");
  
  const handleSubmit = useCallback(async (event) => {
    event.preventDefault();
    // I am passing hardcoded { id: 1 } as an argument. This can 
    // be a value from the state ~ user's input depending on your
    // application's logic.
    await executeFetch({ id: 1 }).then(response => {
      // Here you will access to 
      // data or error from promise. 
      console.log('RESPONSE: ', response);
    })
  }, [executeFetch]);
Run Code Online (Sandbox Code Playgroud)

另一个建议是不要在钩子内立即传递布尔值来触发 executeFetch,由调用者决定是否立即运行 executeFetch。

const { executeFetch, ... } = useFetch(....);
// you can call it immediately after setting the hook if you ever needed
await executeFetch()
Run Code Online (Sandbox Code Playgroud)