React对象的初始状态被覆盖

时间:2020-06-15 09:12:44

标签: javascript reactjs immutability use-state

我有一个带有表单的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;

为什么这里要覆盖初始形式?

2 个答案:

答案 0 :(得分:1)

您已使用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;
    });

答案 1 :(得分:0)

这是深层复制和浅层复制的问题。 “ formSettings”数据源是“ initialForm”。使用'setFormSettings'将更改为'initialForm',这是正确的。因为在初始化时使用浅表副本。您可以使用功能Deep Deep复制到'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()); 
   ... 
}