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

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

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.info(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 (
    <div>
      <button
        onClick = {() =>
          // Update state all in one object.
          s({
            count: 2,
            cool: false,
          })
        }
      >
        Click
      </button>

      {/* Access state via the st variable. */}
      <pre>{JSON.stringify(st, null, 2)}</pre>
    </div>
  )
}

const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)

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

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

Любая помощь будет оценена по достоинству.

похоже, вы присвоили значение и никогда не обновляли его в st[k]

VariableVasasMT 07.04.2019 04:12
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
1
4 229
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

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.info(st[k], obj[k]);
    }

    console.info(st);
  }
};

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

Ответ принят как подходящий

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

Only Call Hooks at the Top Level

Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function.

Only Call Hooks from React Functions

Don’t call Hooks from regular JavaScript functions.

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

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

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

  useEffect(() => {

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

//...

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

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

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

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

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

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

Из любопытства, почему установить st как initial state, а также использовать useEffect (при монтировании компонента) для установки начального состояния?

corysimmons 07.04.2019 07:23

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

Christian C. Salvadó 12.04.2019 22:45

Другие вопросы по теме