React 对象的初始状态被覆盖

React object initial state is overriden

我得到了一个带有表单的 React 组件。我将表单设置保存在组件外部的对象中:

const initialForm = {
name: {
    elementType: 'input',
    elementAtts: {
        label: 'Tenant Name', 
        readOnly: false        
    }, 
    isRequired : true,      
    value: '',
},
description: {
    elementType: 'input',
    elementAtts: {
        label: 'Description',
        readOnly: false 
    },    
    isRequired : false,  
    value: '',
}
}

const AddAndDisplay = (props) => 
{ 
   const [formSettings, setFormSettings] = useState(initialForm); 
   ... 
}

elementAtts是我输入的属性

我想做的是打开一个显示表单的模式 - 一次仅用于显示,一次允许编辑 - 可以用于编辑现有项目或添加新项目。

我这样做是为了编辑现有项目并显示:

//a callback
const OpenModalForEditOrDisplay = (isEditable, cardObject) =>
{
      setFormSettings(prevForm => 
        { 
          let newForm = {...prevForm};
          newForm.name.elementAtts.readOnly = !isEditable;
          newForm.description.elementAtts.readOnly = !isEditable;
          return {...newForm} 
        });
       setIsFormOpen(true);
    }
  };

并添加新项目:

setFormSettings(initialForm); 
setIsEditing(true); 
setIsFormOpen(true); //this is merely a state saying if to show the modal with the form

然后用户可以提交或取消表单,无论哪种情况我都在做:

setFormSettings(initialForm); 

问题是 initialForm 似乎被覆盖了,如果我打开表单仅供显示,当试图打开表单进行添加时它仍然显示,因为编辑部分的代码改变了什么我以为会是initialForm的副本。如果我在打开以供编辑功能中删除这些行,表单将保留初始表单的设置:

newForm.name.elementAtts.readOnly = !isEditable;
newForm.description.elementAtts.readOnly = !isEditable;

为什么初始表单在这里被覆盖了?

您已经使用 Spread 语法在 setFormSettings 中克隆了 prevForm 值。但是您必须注意,Spread 语法仅浅克隆对象并且不执行深度克隆,这意味着您在 prevForm 中嵌套的值仍然保留原始引用,并且当您更新值时

newForm.name.elementAtts.readOnly = !isEditable;
newForm.description.elementAtts.readOnly = !isEditable;

您正在对原始引用进行变异。更新状态的正确方法是通过克隆每个嵌套级别来不可变地更新它,例如

setFormSettings(prevForm => 
    { 
      let newForm = {
           ...prevForm, 
           name: {
              ...prevForm.name,
              elementAttrs: {
                 ...prevForm.name.elementAttrs,
                 readOnly: !isEditable,
              }
           }
           description: {
              ...prevForm.description,
              elementAttrs: {
                 ...prevForm.description.elementAttrs,
                 readOnly: !isEditable,
              }
           }
        };
      return newForm;
    });

这是深拷贝和浅拷贝的问题。 'formSettings' 数据源是 'initialForm'。使用 'setFormSettings' 将更改为 'initialForm' ,这是对的。因为你初始化的时候用的是浅拷贝。您可以使用函数深度复制到 'initialForm'.

const createInitialForm = () => ({
name: {
    elementType: 'input',
    elementAtts: {
        label: 'Tenant Name', 
        readOnly: false        
    }, 
    isRequired : true,      
    value: '',
},
description: {
    elementType: 'input',
    elementAtts: {
        label: 'Description',
        readOnly: false 
    },    
    isRequired : false,  
    value: '',
}
})

const AddAndDisplay = (props) => 
{ 
   const [formSettings, setFormSettings] = useState(createInitialForm()); 
   ... 
}