【问题标题】:useEffect hook not triggered when state is updated somewhere else在其他地方更新状态时未触发 useEffect 挂钩
【发布时间】:2020-08-03 19:21:31
【问题描述】:

我在尝试侦听此应用程序中的状态更改时遇到了一些问题。基本上我希望在某些状态更改后触发 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>
}

好吧,我有这个父组件 Provider 监视会话状态,但是当它更新时,永远不会调用提供程序的 useEffect。

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

useEffect 的回调只被调用一次,当组件被挂载时,而不是在状态改变时。

我怎样才能实现这种行为?每当 session 更新时,ProvideruseEffect 就会被触发?

提前致谢!

【问题讨论】:

  • 试图弄清楚为什么登录函数用useCallback 钩子装饰......没有依赖数组。如果没有我确定它并没有真正记住登录回调。我不认为这是问题,但这很奇怪。您还期望在useSession 中触发对session 的更改除了setter之外还有什么?
  • 同意@DrewReese。它可能必须与 useCallback 一起使用。尝试使用常规功能。如果这没有帮助,代码框会很有帮助。

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


【解决方案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增加count不会影响ComponentB的count实例。这是因为组件的每个实例都有自己的状态 em>。您还可以看到,单击任一按钮都不会触发 ComponentC 重新渲染,因为它们不共享同一个实例。即使我像在组件 D 中那样每隔一秒触发一次重新渲染,因此调用 useCounter 中的计数器 ComponentD 仍然为 0。

解决方案 然而,有多种方法可以让组件共享/监听相同的状态变化

  1. 您可以将所有状态(即 [会话状态])转移到 Provider 组件,并通过 props 传递它使其对其他组件可见。
  2. 您可以将状态移动到全局容器 Redux 或简单地使用 Context Api + UseReducer Hook @987654321 @

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

【讨论】:

  • 非常感谢!这是关于这个主题的另一个有趣的阅读medium.com/@akrush95/… 每次我打电话给useSession 我都在重新创建钩子。我认为它以某种方式在内部保留了这些值。但事实并非如此,所以每次调用都是一个新状态
猜你喜欢
  • 2020-02-12
  • 2019-12-27
  • 2021-02-14
  • 2019-09-23
  • 1970-01-01
  • 2021-07-18
  • 1970-01-01
  • 2023-01-26
  • 2021-03-22
相关资源
最近更新 更多