【问题标题】:useEffect Hook Not Firing After State Change状态更改后未触发 useEffect Hook
【发布时间】:2019-07-04 09:05:41
【问题描述】:

我有两个兄弟组件,它们在 react 中通过上下文共享状态。 组件之间的共享状态是一个数组。

如果我在一个组件中更新arr 状态,我希望另一个组件监听该更新并做相应的事情。当我在第二个组件中使用useEffect 时,我会监听arr 状态变量的变化。

例如:

// App Component -------
const App = props => {
 const { arr, setArr } = useContext(GlobalContext)

 const handleChange = () => {
   const newArr = arr
   [10, 20, 30, 40].map(v => {
     newArr.push(v)
     setArr(newArr)
   })

  return (...)
}

// App2 (Sibling) Component 
const App2 = props => {
  const { arr, setArr } = useContext(GlobalContext)
  const [localArr, setLocalArr] = useState(0)

  useEffect(
    () => {
      updateLocalState()
    },
    // fire if "arr" gets updated
    [arr]
  )

  const updateLocalState = () => {
    setLocalArr(localArr + 1)
  }

  return (...)
}

useEffect 钩子在初始渲染时触发,尽管arr 的状态会更新。

我知道向我的状态变量声明一个新变量const newArr = arr 是一个引用,所以newArr.push(v) 在技术上是一个状态突变。 但是,状态仍然更新,没有抛出警告,useEffect 什么也不做。

为什么状态更新后useEffect 没有被调用?是因为状态突变吗?

第二个问题:为什么没有关于状态突变的警告或错误?状态突变是危险的 - 如果它发生,我希望得到某种警告。

现场演示:

【问题讨论】:

  • 您永远不会创建新数组。 const newArr = arr 用另一个变量引用同一个数组。尝试例如const newArr = [...arr] 代替。
  • 它是对同一个数组的引用,但这并不能解释为什么 useEffect 不会触发,尽管状态仍在更新。
  • 您作为第二个参数传递给useEffect 的数组仅检查数组中的元素是否为=== 到上一次渲染中的元素。 const newArr = arr; 会导致newArr === arr,这不是你想要的。
  • @Tholle 所说的,只是更改 setArr([...newArr]) 会正确触发 useEffect

标签: javascript reactjs react-hooks


【解决方案1】:

您作为第二个参数传递给useEffect 的数组仅检查数组中的元素是否为=== 到上一次渲染中的元素。 const newArr = arr; 将导致 newArr === arr 因为它不会创建新数组,这不是您想要的。

使用arr 中的所有元素创建一个新数组,它将按预期工作。

const App = props => {
 const { arr, setArr } = useContext(GlobalContext)

 const handleChange = () => {
   const newArr = [...arr]
   [10, 20, 30, 40].forEach(v => {
     newArr.push(v)
   })
   setArr(newArr)
 }

  return <>{/* ... */}</>
}

【讨论】:

  • 我认为可能值得展示这个代码块的更好实践——就像setArr([...arr, 10, 20, 30, 40])
  • 我没有认为 useEffect 中的差异算法是一个严格的比较,并且它是那个比较的同一个对象。这是有道理的。
  • 我想知道为什么没有关于状态突变的错误或警告?
  • @lsimonetti 遗憾的是,React 没有直接的方法来检查突变,更不用说在你这样做时发出警告了。这只是语言的限制。
【解决方案2】:

当您想使用 useState 挂钩更新数组时。确保将数组传播到新数组并更新新数组,以便调用监听此状态的 useEffect。

UseEffect 不会在下面的代码 sn-p 中调用,因为您是直接更新数组。

const [skills, selectedSkills] = useState([])

     const onSelect = (selectedList) => {
            selectedSkills(selectedList)
        }
     useEffect(() => {
            MyLogger('useEffect called')
        }, [skills])

UseEffect 将在下面的代码 sn-p 中调用,因为我们保持对数组的新引用。

const [skills, selectedSkills] = useState([])

     const onSelect = (selectedList) => {
            const tempSelectedList = [...selectedList]
            selectedSkills(tempSelectedList)
        }
     useEffect(() => {
            MyLogger('useEffect called')
        }, [skills])

【讨论】:

    猜你喜欢
    • 2020-11-06
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 2020-09-07
    • 2021-02-09
    • 2021-07-18
    • 2021-07-03
    • 1970-01-01
    相关资源
    最近更新 更多