简体   繁体   English

当 state 在其他地方更新时未触发 useEffect 挂钩

[英]useEffect hook not triggered when state is updated somewhere else

I`m having some problems trying to listen to state changes in this application.我在尝试收听此应用程序中的 state 更改时遇到了一些问题。 Basically I was expecting a useEffect hook to be fired after some state changed, but nothing at all is happening.基本上我期待在一些 state 改变后会触发一个 useEffect 钩子,但什么都没有发生。

This is what I got这就是我得到的

index.jsx索引.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使用Session.jsx

export const useSession = () => {

  const [session, setSession] = useState(null)

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

  return {
    session,
    setSession,
    login
  }
}

Provider.jsx提供者.jsx

const { session } = useSession();

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

// more code ...

App.jsx应用程序.jsx

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

Well I have this Parent component Provider watching the session state, but when it is updated the useEffect of the provider is never called.好吧,我有这个父组件 Provider 正在监视 session state,但是当它更新时,永远不会调用提供者的 useEffect。

The useEffect is fired only if the setSession is called in the same hook/method.仅当setSession在相同的钩子/方法中调用时才会触发useEffect For example, if I import the setSession in the Provider and use it there, the useEffect will be fired;例如,如果我在Provider中导入setSession并在那里使用它,则将触发useEffect Or if I add a useEffect in the useSession method, it is gonna be fired when login updates the state.或者,如果我在useSession方法中添加一个useEffect ,它将在login更新 state 时被触发。

The callback of useEffect is called but only once, when the component is mounted, but not when the state is changed. useEffect的回调只被调用一次,当组件被挂载时,而不是当 state 被改变时。

How can I achieve this behavior?我怎样才能实现这种行为? Having the Provider 's useEffect fired whenever session is updated?每次更新session时都会触发ProvideruseEffect吗?

Thanks in advance!提前致谢!

I think this is just a bit of misunderstanding of how custom hooks work.Every instance of the component has its own state.我认为这只是对自定义钩子如何工作的一点误解。组件的每个实例都有自己的 state。 Let me just show a simple example illustrating this.让我举一个简单的例子来说明这一点。

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>
  )
}


From the above code if you can see that incrementing count by clicking Button A will not affect the count instance of ComponentB .This is because every instance of the component has its own state .从上面的代码中可以看出,点击Button A增加计数不会影响ComponentB的计数实例。这是因为组件的每个实例都有自己的 state You can also see that clicking either buttons won't trigger ComponentC to rerender since they don't share the same instance.您还可以看到,单击任一按钮都不会触发ComponentC重新呈现,因为它们不共享相同的实例。 Even if i trigger rerender every one second like in Component D thus invoking useCounter the counter in ComponentD remains 0.即使我像在组件 D 中那样每隔一秒触发一次重新渲染,从而调用useCounterComponentD D 中的计数器仍然为 0。

Solution However there are multiple ways of making components share/listen to same state changes解决方案然而,有多种方法可以让组件共享/监听相同的 state 更改

  1. You can shift all your state ie [session state] to the Provider component and make it visible to other components by passing it via props.您可以将所有 state 即 [会话状态] 转移到Provider组件,并通过道具传递它使其对其他组件可见。
  2. You can move state to a global container Redux or simply use Context Api + UseReducer Hook here is an example您可以将 state 移动到全局容器Redux或简单地使用Context Api + UseReducer Hook 这是一个示例

But since you are dealing with auth and session management, I suggest you persist the session state in local storage or session storage, and retrieve it whenever you need it. But since you are dealing with auth and session management, I suggest you persist the session state in local storage or session storage, and retrieve it whenever you need it. Hope that helped希望有帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM