Я пытаюсь создать небольшую библиотеку для очистки этого шаблона.
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.
Любая помощь будет оценена по достоинству.
попробуй что-нибудь вроде этого
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.Component
setState
:
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, отредактировал мой ответ с учетом вашего комментария, в основном вы можете установить начальное состояние любым из двух способов, любым из которых вам больше подходит, а также добавил пример поведения этого состояния, извлеченного в пользовательский хук. Ваше здоровье.
похоже, вы присвоили значение и никогда не обновляли его в
st[k]