在其他地方更新状态时,不会触发useEffect挂钩

时间:2020-04-21 06:00:43

标签: javascript reactjs react-hooks react-context use-effect

我在尝试监听此应用程序中的状态更改时遇到一些问题。基本上,我期望在某些状态更改后会触发useEffect挂钩,但是什么也没有发生。

这就是我所得到的

index.jsx

// this is a simplification.
// I actually have a react-router-dom's Router wrapping everything 
// and App is a Switch with multiple Route components
ReactDOM.render(
<Provider>
    <App>
</Provider>
, document.getElementById('root'));

useSession.jsx

export const useSession = () => {

  const [session, setSession] = useState(null)

  const login = useCallback(() => {
     // do something
     setSession(newSession)
  })

  return {
    session,
    setSession,
    login
  }
}

Provider.jsx

const { session } = useSession();

useEffect(() => {
  console.log('Print this')
}, [session])

// more code ...

App.jsx

export function Login() {
    const { login } = useSession();
    return <button type="button" onClick={() => { login() }}>Login</button>
}

我让这个Parent组件Provider监视会话状态,但是当它更新时,永远不会调用该提供者的useEffect。

只有在相同的挂钩/方法中调用useEffect时才会触发setSession。例如,如果我将setSession导入Provider并在其中使用,则会触发useEffect;或者,如果我在useEffect方法中添加useSession,则login更新状态时将触发它。

useEffect的回调仅在安装组件时被调用一次,而在状态改变时仅被调用。

如何实现此行为?每当Provider更新时是否触发useEffect的{​​{1}}?

谢谢!

1 个答案:

答案 0 :(得分:1)

我认为这只是对自定义钩子工作方式的误解。每个组件实例都有其自己的状态。让我只显示一个简单的例子来说明这一点。

function App () {
  return (
    <div>
      <ComponentA/>
      <ComponentB/>
      <ComponentC/>
      <ComponentD/>
    </div>
  )
}

function useCounter() {
  const [counter, setCounter] = React.useState(0);

  function increment() {
    setCounter(counter+1)
  }

  return {
    increment, counter, setCounter
  }
}

function ComponentA() {
  const { counter, increment }= useCounter()
  return (
    <div>
      <button onClick={()=>increment()}>Button A</button>
      ComponentA Counter: {counter}
    </div>
  )
}

function ComponentB() {
  const { counter, increment }= useCounter()
  return (
    <div>
      <button onClick={()=>increment()}>Button B</button>
      ComponentB Counter: {counter}
    </div>
  )
}

function ComponentC() {
  const { counter }= useCounter();
  return (
    <div>
      ComponentC Counter: {counter}
    </div>
  )
}

function ComponentD() {
  const [toggle, setToggle] = React.UseState(false);
  const { counter }= useCounter();

  React.useEffect(() => {
     setInterval(()=>{
       setToggle(prev => !prev);
     }, 1000)
  })

  return (
    <div>
      ComponentD Counter: {counter}
    </div>
  )
}


从上面的代码中,如果您可以看到通过单击Button A来增加计数不会影响ComponentB的计数实例。这是因为 该组件的每个实例都有其自己的状态 。您还可以看到单击任何一个按钮都不会触发ComponentC,因为它们不共享同一实例。即使我像组件D中那样每秒触发一次重新渲染,从而调用useCounterComponentD中的计数器仍为0。

解决方案 但是,有多种方法可以使组件共享/监听相同的状态更改

  1. 您可以将所有状态(即[会话状态])转移到Provider组件,并通过道具将其传递给其他组件。
  2. 您可以将状态移动到全局容器 Redux 或仅使用 Context Api + UseReducer Hook {{ 3}}

但是,由于您要处理身份验证和会话管理,因此建议您将会话状态保留在本地存储或会话存储中,并在需要时将其检索。希望有帮助