Реагирование: зацикливание на объекте для привязки функций useState не обновляет состояние моего приложения - PullRequest
1 голос
/ 07 апреля 2019

Я пытаюсь создать небольшую библиотеку для очистки этого шаблона ..

const [a, setA] = useState(1)
const [b, setB] = useState(2)
const [c, setC] = useState(3)
const [d, setD] = useState(4)
const [e, setE] = useState(5)
// ...

Во что-то похожее на это ...

s({
  a: 1,
  b: 2,
  c: 3,
  d: 4,
  e: 5
}, true)

Моя идея состояла в том, чтобызациклите объект, который передается в функцию s(), и добавьте каждый ключ / значение к объекту, удерживающему состояние, следующим образом:

st = {
  a: 1,
  setA: THE 2nd ITEM IN THE ARRAY THE useState() FUNCTION RETURNS
  // repeat this pattern for b, c, d, e...
}

Кажется, это работает в том смысле, что st.a возвращает1, и st.setA возвращает function bound dispatchAction() (я предполагаю, что это то, что useState()[1] возвращает).

Но когда вы нажимаете кнопку в демоверсии ниже, она не обновляет ни одного изst.

(в приведенном ниже примере я использую count и cool в качестве переменных вместо a, b, c, d, e)

import React, { useState } from 'react'
import ReactDOM from 'react-dom'

// State holder. This could be pulled out to Context for global state.
const st = {}

const s = (obj, setDefaults = false) => {
  if (setDefaults) {
    // Loop over s({...}, true) to create default state.
    Object.keys(obj).map(k => {
      st[k] = useState(obj[k])[0]
      st[`set${k.charAt(0).toUpperCase()}${k.slice(1)}`] = useState(obj[k])[1]
      return null
    })
  } else {
    // Update state using st[setXyz(...)]

    // FIXME: This doesn't update the state.
    // It seems like it would since it is calling things like setCount(2)
    // using the setCount that has a function bound to it in the `st` object.

    for (let k in obj) {
      st[`set${k.charAt(0).toUpperCase()}${k.slice(1)}`](obj[k])
    }

    console.log(st)
  }
}

const App = () => {
  // Set default state object.
  // It's prettier than 20 lines of `const [x, setX] = useState(1)`
  s(
    {
      count: 0,
      cool: true,
    },
    true,
  )

  return (
    
      

      {/* Access state via the st variable. */}
      {JSON.stringify(st, null, 2)}
)}const rootElement = document.getElementById ('root') ReactDOM.render (, rootElement)

https://codesandbox.io/s/14xjzl8xx7 <- Демонстрационная версия </p>

Я подозреваю, что не правильно связывается, или может быть что-то странное в том, как работают React Hooks.

Буду признателен за любую помощь.

Ответы [ 2 ]

1 голос
/ 07 апреля 2019

Вы нарушаете правила хуков :

Только вызовы на верхнем уровне

Не вызывать Хуки внутри циклов, условий или вложенных функций. Вместо этого всегда используйте Hooks на верхнем уровне вашей функции React.

Только вызовы из функций реагирования

Не вызывайте хуки из обычных функций JavaScript.

Для компонентов, которые используют состояния большего размера, я бы порекомендовал вам использовать вместо этого useReducer.

Например, вы можете использовать хук useReducer, чтобы иметь функцию setState, которая может объединять несколько значений свойств одновременно с вашим состоянием:

//...
const App = () => {
  const [state, setState] = useReducer(
    (state, newState) => ({ ...state, ...newState }),
    st
  );

  useEffect(() => {

    setState({
      count: 0,
      cool: true
    });

  }, []);

//...

Как видите, вы можете установить начальное состояние флага useReducer, когда компонент монтируется (в вызове useEffect в приведенном выше примере).

Вы также можете извлечь это поведение состояния в пользовательский хук, я считаю его очень полезным при рефакторинге компонентов, основанных на классах, потому что он имитирует поведение метода React.Component setState:

function useSetState(initialState) {
  const [state, setState] = useReducer(
    (state, newState) => ({...state, ...newState}),
    initialState,
  )
  return [state, setState]
}

И поэтому вы можете просто использовать вышеуказанный пользовательский хук, как если бы вы использовали setState в компонентах на основе классов.

Проверьте эту ветку CodeSandbox и следующие статьи:

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

1 голос
/ 07 апреля 2019

попробуйте что-то вроде этого

const s = (obj, setDefaults = false) => {
  if (setDefaults) {
    // Loop over s({...}, true) to create default state.
    Object.keys(obj).map(k => {
      st[k] = useState(obj[k]);
      return null;
    });
  } else {
    // Update state using st[setXyz(...)]

    // FIXME: This doesn't update the state.
    // It seems like it would since it is calling things like setCount(2)
    // using the setCount that has a function bound to it in the `st` object.

    for (let k in obj) {
      st[k][1](obj[k]);
      console.log(st[k], obj[k]);
    }

    console.log(st);
  }
};

окончательная рабочая ссылка: https://codesandbox.io/s/qx6r8387x4

...