简体   繁体   English

反应 object 初始 state 被覆盖

[英]React object initial state is overriden

I got a react component with a form.我得到了一个带有表单的反应组件。 I keep the form settings in an object outside the component:我将表单设置保存在组件外部的 object 中:

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); 
   ... 
}

the elementAtts is the attributes I pass the input. elementAtts是我传递输入的属性。

What I'm trying to do is open a modal which displays the form - one time for display only and one time with editing allowed - can be for editing an existing item or for adding a new item.我想要做的是打开一个显示表单的模式 - 一次仅用于显示,一次允许编辑 - 可以用于编辑现有项目或添加新项目。

I'm doing it like this for editing an existing item and for displaying:我这样做是为了编辑现有项目并显示:

//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);
    }
  };

and for adding a new item:并添加一个新项目:

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

The user can then submit or cancel the form, and on either case I'm doing:然后用户可以提交或取消表单,无论哪种情况我都在做:

setFormSettings(initialForm); 

The problem is that it seems like initialForm is overridden and if I open the form for display only, it stays on display when trying to open the form for addition because the code for the edit part changed what I thought would be a copy of the initialForm .问题是initialForm似乎被覆盖了,如果我只打开表单进行显示,它会在尝试打开表单进行添加时保持显示状态,因为编辑部分的代码改变了我认为是initialForm的副本. If I remove these lines in the open for edit function the form stays with the initial form's settings:如果我在打开编辑 function 中删除这些行,则表单将保留初始表单的设置:

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

Why is the initial form being overridden here?为什么这里会覆盖初始形式?

You have used Spread syntax to clone the prevForm values within setFormSettings.您已使用 Spread 语法在 setFormSettings 中克隆 prevForm 值。 However you must note that Spread syntax only shallow clones the object and does not perform a deep cloning which means that you nested values within the prevForm still hold the original reference and when you update the values like但是您必须注意,Spread 语法仅对 object 进行浅层克隆,并且不执行深层克隆,这意味着您在 prevForm 中的嵌套值仍保留原始引用,并且当您更新值时,例如

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

You are mutating it at the original reference.您正在原始参考中对其进行变异。 The correct way to update state is to immutably update it by cloning each nested level like更新 state 的正确方法是通过克隆每个嵌套级别来不变地更新它,例如

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;
    });

This is a problem for Deep Copy and shallow copy.这是深拷贝和浅拷贝的问题。 The 'formSettings' data source is 'initialForm'. “formSettings”数据源是“initialForm”。 Use 'setFormSettings' will to change 'initialForm', this is right.使用'setFormSettings' 将改变'initialForm',这是正确的。 Because you use a shallow copy when you initialize it.因为你在初始化的时候使用了浅拷贝。 you can use a function Deep Copy to 'initialForm'.您可以使用 function 深度复制到“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()); 
   ... 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM