反应在状态中使用值与在对象中使用值的区别
React difference of using value in state vs value in object in state
根据我是在 useState 上使用 bool 值,还是在 useState 对象内部使用 bool 值,我会得到不同的行为。
第一段代码将在按下按钮时显示隐藏文本。它直接在状态上使用布尔值 contextMenuIsOpen 来控制隐藏文本的可见性。
const Parent = () => {
const [contextMenuState, setContextMenuState] = useState({ isOpen: false, x: 0, y: 0, clipboard:null });
const [contextMenuIsOpen, setContextMenuIsOpen] = useState(false);
const openChild = ()=>{
setContextMenuIsOpen(true);
}
return <div><h1>Hello</h1>
<button onClick={openChild}>Open Child</button>
{contextMenuIsOpen &&
<h1>hidden</h1> }
</div>
}
export default Parent;
下一段代码在状态上的对象上使用 属性。当我这样做时它不显示隐藏文本。
const Parent = () => {
const [contextMenuState, setContextMenuState] = useState({ isOpen: false, x: 0, y: 0, clipboard:null });
const [contextMenuIsOpen, setContextMenuIsOpen] = useState(false);
const openChild = ()=>{
contextMenuState.isOpen = true;
setContextMenuState(contextMenuState);
}
return <div><h1>Hello</h1>
<button onClick={openChild}>Open Child</button>
{contextMenuState.isOpen &&
<h1>hidden</h1> }
</div>
}
export default Parent;
状态在反应中是不可变的。
您必须使用 setContextMenuState()
来更新状态值。
因为要根据之前的状态更新状态,所以最好在setContextMenuState
中传入一个箭头函数,其中prev
是之前的状态。
const openChild = () =>{
setContextMenuState((prev) => ({...prev, isOpen: true }))
}
尝试改变
contextMenuState.isOpen = true;
至:
setContextMenuState((i) => ({...i, isOpen: true}) )
永远不要像这样改变状态'contextMenuState.isOpen = true;'
React 通过检查对象的引用来检查对象是否相等。
简单的看下面的例子
const x = { a : 1, b : 2};
x.a = 3;
console.log(x===x);
所以当你执行以下操作时,
const openChild = ()=>{
contextMenuState.isOpen = true;
setContextMenuState(contextMenuState);
}
您没有更改 contextMenuState
的引用。因此,没有真正的状态变化,setContextMenuState
不会导致任何重新渲染。
解法:
创建一个新的引用。
一个例子是使用扩展运算符:
const openChild = ()=>{
setContextMenuState({ ...contextMenuState , isOpen : true });
}
第二种方法的问题是 React 不会识别值已更改。
const openChild = () => {
contextMenuState.isOpen = true;
setContextMenuState(contextMenuState);
}
在此代码中,您引用了对象的字段,但对象引用本身并没有改变。 React 仅检测到 contextMenuState
指向与之前相同的地址,并且从它的角度来看没有任何变化,因此无需重新渲染任何内容。
如果您像这样更改代码,将创建一个新对象,并且旧的 contextMenuState 不等于新的 contextMenuState
,因为 Javascript 已经创建了一个具有新地址的新对象内存(即 oldContextMenuState
!== newContextMenuState
).:
const openChild = () => {
setContextMenuState({
...contextMenuState,
isOpen: true
});
}
这样 React 将识别状态变化并重新渲染。
根据我是在 useState 上使用 bool 值,还是在 useState 对象内部使用 bool 值,我会得到不同的行为。
第一段代码将在按下按钮时显示隐藏文本。它直接在状态上使用布尔值 contextMenuIsOpen 来控制隐藏文本的可见性。
const Parent = () => {
const [contextMenuState, setContextMenuState] = useState({ isOpen: false, x: 0, y: 0, clipboard:null });
const [contextMenuIsOpen, setContextMenuIsOpen] = useState(false);
const openChild = ()=>{
setContextMenuIsOpen(true);
}
return <div><h1>Hello</h1>
<button onClick={openChild}>Open Child</button>
{contextMenuIsOpen &&
<h1>hidden</h1> }
</div>
}
export default Parent;
下一段代码在状态上的对象上使用 属性。当我这样做时它不显示隐藏文本。
const Parent = () => {
const [contextMenuState, setContextMenuState] = useState({ isOpen: false, x: 0, y: 0, clipboard:null });
const [contextMenuIsOpen, setContextMenuIsOpen] = useState(false);
const openChild = ()=>{
contextMenuState.isOpen = true;
setContextMenuState(contextMenuState);
}
return <div><h1>Hello</h1>
<button onClick={openChild}>Open Child</button>
{contextMenuState.isOpen &&
<h1>hidden</h1> }
</div>
}
export default Parent;
状态在反应中是不可变的。
您必须使用 setContextMenuState()
来更新状态值。
因为要根据之前的状态更新状态,所以最好在setContextMenuState
中传入一个箭头函数,其中prev
是之前的状态。
const openChild = () =>{
setContextMenuState((prev) => ({...prev, isOpen: true }))
}
尝试改变
contextMenuState.isOpen = true;
至:
setContextMenuState((i) => ({...i, isOpen: true}) )
永远不要像这样改变状态'contextMenuState.isOpen = true;'
React 通过检查对象的引用来检查对象是否相等。
简单的看下面的例子
const x = { a : 1, b : 2};
x.a = 3;
console.log(x===x);
所以当你执行以下操作时,
const openChild = ()=>{
contextMenuState.isOpen = true;
setContextMenuState(contextMenuState);
}
您没有更改 contextMenuState
的引用。因此,没有真正的状态变化,setContextMenuState
不会导致任何重新渲染。
解法:
创建一个新的引用。 一个例子是使用扩展运算符:
const openChild = ()=>{
setContextMenuState({ ...contextMenuState , isOpen : true });
}
第二种方法的问题是 React 不会识别值已更改。
const openChild = () => {
contextMenuState.isOpen = true;
setContextMenuState(contextMenuState);
}
在此代码中,您引用了对象的字段,但对象引用本身并没有改变。 React 仅检测到 contextMenuState
指向与之前相同的地址,并且从它的角度来看没有任何变化,因此无需重新渲染任何内容。
如果您像这样更改代码,将创建一个新对象,并且旧的 contextMenuState 不等于新的 contextMenuState
,因为 Javascript 已经创建了一个具有新地址的新对象内存(即 oldContextMenuState
!== newContextMenuState
).:
const openChild = () => {
setContextMenuState({
...contextMenuState,
isOpen: true
});
}
这样 React 将识别状态变化并重新渲染。