我正在尝试创建一个小型库来清理此模式。
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
用作var而不是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 (
<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的工作方式可能有些奇怪。
任何帮助将不胜感激。
答案 0 :(得分:1)
尝试这样的事情
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);
}
};
答案 1 :(得分:1)
您正在破坏rules of hooks:
仅位于顶层的呼叫挂钩
请勿在循环,条件或嵌套函数中调用Hook。相反,请始终在您的React函数的顶层使用挂钩。
仅来自React函数的呼叫挂钩
不要从常规JavaScript函数中调用Hook。
对于使用较大状态的组件,我建议您改为使用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
一样简单地使用上述自定义钩子。
检查this CodeSandbox fork和以下文章:
此外,忘了提及您的“状态持有者”对象,我建议您将其作为组件的“初始状态”,这就是为什么在我的示例中将其传递给useReducer
的原因钩。由于我们让React处理状态,所以我们不应该直接对该对象进行更改,因此组件的“当前”状态将存在于我们在示例中呈现的state
标识符中。