useEffect挂钩在状态更改后未触发

时间:2019-02-10 20:48:31

标签: javascript reactjs react-hooks

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

如果我在一个组件中更新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没有被调用?是因为状态突变了吗?

第二个问题:为什么没有关于状态突变的警告或错误引发?状态突变很危险-如果发生这种情况,我会期望发出某种警告。

现场演示:

Edit 7wzlo8y4m1

2 个答案:

答案 0 :(得分:3)

当你想使用 useState 钩子更新数组时。确保将数组扩展到新数组并更新新数组,以便调用监听此状态的 useEffect。

UseEffect 不会在下面的代码片段中调用,因为您正在直接更新数组。

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

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

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

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

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

答案 1 :(得分:2)

作为第二个参数传递给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 <>{/* ... */}</>
}