Хук useEffect не срабатывает при обновлении состояния в другом месте - PullRequest
0 голосов
/ 21 апреля 2020

У меня возникли проблемы при попытке прослушивания изменений состояния в этом приложении. В основном я ожидал, что хук 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>
}

Хорошо, у меня есть этот Поставщик родительского компонента, наблюдающий за состоянием сеанса, но когда он обновляется, useEffect провайдера никогда не вызывается.

Вызывается только useEffect если setSession вызывается в том же хуке / методе. Например, если я импортирую setSession в Provider и использую его там, useEffect будет запущен; Или, если я добавлю useEffect в метод useSession, он будет запущен, когда login обновит состояние.

Вызывается обратный вызов useEffect, но только один раз, когда компонент смонтирован, но не при изменении состояния.

Как мне добиться этого поведения? * * * * * * * * * * * * * * useEffect Выстрел *1041* *1041* всякий раз, когда session обновляется?

Заранее спасибо!

1 Ответ

1 голос
/ 21 апреля 2020

Я думаю, что это всего лишь небольшое недопонимание того, как работают пользовательские хуки. Каждый экземпляр компонента имеет свое собственное состояние. Позвольте мне показать простой пример, иллюстрирующий это.

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, таким образом, вызывая useCounter, счетчик в ComponentD остается равным 0.

Решение Однако существует несколько способов заставить компоненты делиться / слушать к тем же изменениям состояния

  1. Вы можете переместить все свое состояние, т.е. [состояние сеанса], на компонент Provider и сделать его видимым для других компонентов, передав его через подпорки.
  2. You можно переместить состояние в глобальный контейнер Redux или просто использовать Context Api + UseReducer Hook вот пример

Но поскольку вы имеете дело с управлением аутентификацией и сеансом, я предлагаю вам сохранить состояние сеанса в локальном хранилище или хранилище сеанса и извлекать его всякий раз, когда вам это нужно. Надеюсь, что помог

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...