系列
什么是虫洞状态管理模式?
您可以逃脱的最小 state 共享量是多少?
保持你的 state。尽可能靠近使用它的地方。
如果有一个组件关心这个问题,使用它。如果有几个组件在意,就用 props 分享一下。 如果很多组件都关心,把它放在 context 中。
Context 就像一个虫洞。它使您的组件树弯曲,因此相距很远的部分可以接触。
利用自定义 hooks 使这变得容易。
一个例子
构建一个点击计数器。虫洞状态管理模式最好通过示例来解释 ✌️
CodeSandbox(示例代码)
步骤 1
我们从 useState 开始,因为它是最简单的。
const ClickCounter = () => {
const [count, setCount] = useState(0);
function onClick() {
setCount(count => count + 1);
}
return <button onClick={onClick}>{count} +1</button>;
};
count 保存当前的点击次数,setCount 让我们在每次点击时更新值。
足够简单。
不过,外观并不是很漂亮。让我们用一个自定义按钮组件和一些嵌套来改进它。
步骤 2
我们创建了一个可重复使用的 PrettyButton,确保您应用中的每个按钮看起来都很棒。
状态保留在 ClickCounter 组件中。
const ClickCounter = () => {
const [count, setCount] = useState(0);
function onClick() {
setCount(count => count + 1);
}
return (
<>
<p>You have clicked buttons {count} times</p>
<div style={{ textAlign: "right" }}>
<PrettyButton onClick={onClick}>+1</PrettyButton>
</div>
</>
);
};
这是必要的最少状态共享。我们也保持了简单的状态。
计数器组件关心点击次数和计数,因此它将回调作为 props 传递到按钮中。函数被调用,状态更新,组件重新渲染。
不需要复杂的操作。
步骤 3
如果我们的状态更复杂怎么办? 我们有 2 个属于一起的项。
您可以在您的状态中保留复杂的值。效果很好。
const ClickCounter = () => {
const [count, setCount] = useState({ A: 0, B: 0 });
function onClickA() {
setCount(count => {
return { ...count, A: count.A + 1 };
});
}
function onClickB() {
setCount(count => {
return { ...count, B: count.B + 1 };
});
}
return (
<>
<p>
You have clicked buttons A: {count.A}, B: {count.B} times
</p>
<div style={{ textAlign: "right" }}>
<PrettyButton onClick={onClickA}>A +1</PrettyButton>
<PrettyButton onClick={onClickB}>B +1</PrettyButton>
</div>
</>
);
};
我们已将 count 拆分为一个对象 – { A, B }。
现在单个状态可以保存多个值。单独按钮点击的单独计数。
React 使用 JavaScript 相等来检测重新渲染的更改,因此您必须在每次更新时制作完整状态的副本。这在大约 10,000 个元素时变慢。
您也可以在这里使用 useReducer。
特别是当您的状态变得更加复杂并且项目经常单独更新时。
使用 useReducer 的类似状态如下所示:
const [state, dispatch] = useReducer((action, state) => {
switch (action.type) {
case \'A\':
return { ...state, A: state.A + 1 }
case \'B\':
return { ...state, A: state.A + 1 }
}
}, { A: 0, B: 0})
function onClickA() {
dispatch({ type: \'A\' })
}
你的状态越复杂,这就越有意义。
但我认为那些 switch 语句很快就会变得混乱,而且你的回调函数无论如何都已经是动作了。
步骤 4
如果我们想要 2 个按钮更新相同的状态怎么办?
您可以将 count 和 setCount 作为 props 传递给您的组件。但这变得越来越混乱。
const AlternativeClick = ({ count, setCount }) => {
function onClick() {
setCount(count => {
return { ...count, B: count.B + 1 };
});
}
return (
<div style={{ textAlign: "left" }}>
You can also update B here
<br />
<PrettyButton onClick={onClick}>B +1</PrettyButton>
<p>It\'s {count.B} btw</p>
</div>
);
};
我们创建了一个难以移动并且需要理解太多父逻辑的组件。关注点是分裂的,抽象是奇怪的,我们造成了混乱。
你可以通过只传递它需要的状态部分和一个更自定义的 setCount 来修复它。但这是很多工作。
步骤 5
相反,您可以使用虫洞与自定义 hook 共享状态。