在useEffect或外部定义函数?

时间:2020-04-30 16:08:36

标签: javascript reactjs react-hooks next.js

为什么fetchData函数定义在useEffect内部而不是外部?

链接: https://github.com/zeit/next.js/blob/canary/examples/with-graphql-faunadb/lib/useFetch.js

import { useState, useEffect } from 'react'

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

  useEffect(() => {
    const fetchData = async () => {
      try {
        const res = await fetch(url, options)
        const json = await res.json()

        setData(json)
      } catch (error) {
        setError(error)
      }
    }
    fetchData()
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [url])

  return { data, error }
}

我会那样做的:

import { useState, useEffect } from 'react'

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

  // Defined outside of useEffect
  // `u` instead of `url` for not overlapping
  // with the one passed in useFetch()
  const fetchData = async (u) => {
    try {
      const res = await fetch(u, options)
      const json = await res.json()

      setData(json)
    } catch (error) 
      setError(error)
    }
  }

  useEffect(() => {
    // Using url as an argument
    fetchData(url)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [url])

  return { data, error }
}

阅读起来似乎更容易,组织起来也更好。我想这可能是反模式或其他?

2 个答案:

答案 0 :(得分:1)

我通常在useEffect内定义函数,这有几个原因

  1. 通过在使用效果之外定义功能,您要么需要禁用详尽的探视并意外地拥有过时的功能,要么需要使用Callback使该功能不对每个渲染器都进行更新
  2. 如果仅在useEffect中使用该函数,则无需在每个渲染器上重新创建该函数,因为这只是浪费的循环
  3. 通过在useEffect中定义异步函数,可以更轻松地对异步函数进行清理,因为您可以定义可以在效果中修改的变量。

例如,在最后一个上,您可以执行一些操作以防止在效果清除时调用状态。

您还可以将AbortController与fetch一起使用来取消提取。

import { useState, useEffect } from 'react'

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

  useEffect(() => {
    let isUnmounted = false;
    const fetchData = async () => {
      try {
        const res = await fetch(url, options)
        const json = await res.json()
        if(!isUnmounted) setData(json)
      } catch (error) {
        if(!isUnmounted) setError(error)
      }
    }
    fetchData()
    return ()=>{isUnmounted = true;}
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [url])

  return { data, error }
}

答案 1 :(得分:0)

从技术上讲,根据React Hooks规则,fetchData应该是useEffect的依赖项。但是,如果添加了它,它将给您一个错误,指出如果在组件内部定义了此钩子,则在重新创建函数时,useEffect将在每次重新渲染时运行。

但是由于它是在组件外部定义的,所以我的理解是该函数不会被重新创建。然后只需将fetchData添加为依赖项即可。

如果在组件内部使用了useEffect,则可以只在useEffect内传递函数,也可以添加依赖项并使用useCallback覆盖fetchData。