反应在状态中使用值与在对象中使用值的区别

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 将识别状态变化并重新渲染。