更改儿童组件中的数据而不在父母中更改它

发布于 2025-01-21 09:59:51 字数 1079 浏览 0 评论 0原文

我正在更改一组数据,并且数据保存在父组件状态下,我将数据传递给第二个组件,但是当我对第二组件中的数据进行更改时,它将在第一个更改数据。 IVE试图为状态的值设置一个变量,以制作IT /还会在第二个组件中创建新状态,该状态是从第一个数据从初始数据设置的。但是由于某种原因,我仍在更改初始数据。谁能解释这里发生了什么?

父组件:

const [data, setData] = useState([]);
const [editing, setEditing] = useState(null);

{!!editing && (
        <EditInsurance state={editing} data={data} setEditing={setEditing} />
)}

第二个组件:

const [update, setUpdate] = useState([]);
const [List, setList] = useState([]);

//sets the data onLoad
 useEffect(() => {
    setList(data);
  }, []);

//edits the data
const handleClick = (item) => {
    let list = List;
    
    list.forEach((el) => {
      if (el.payerName === item.payerName) {
        el.state.push(state.value);
        if (!el.isActive) {
          el.isActive = true;
        }
      }
    });

    //update is an array containing only edited objects.
    setUpdate([...update, item]);
  };

handleclick触发时,我会在已更新的父组件中安装data值。而且我不想更改该数据,直到用户单击保存为止。我如何将数据独有的数据副本不受操纵的数据?

I am making changes to a set of data and the data is held in a parent components state I pass that data to a 2nd component but when I make changes to the data in the 2nd component it is changing the data in the first. Ive tried setting a variable to the value of the state in an effort to make a copy of it / also creating a new state in the 2nd component that is set from the initial data from the first. But For some reason I am still changing the initial data. Can anyone explain whats going on here?

Parent Component:

const [data, setData] = useState([]);
const [editing, setEditing] = useState(null);

{!!editing && (
        <EditInsurance state={editing} data={data} setEditing={setEditing} />
)}

2nd Component:

const [update, setUpdate] = useState([]);
const [List, setList] = useState([]);

//sets the data onLoad
 useEffect(() => {
    setList(data);
  }, []);

//edits the data
const handleClick = (item) => {
    let list = List;
    
    list.forEach((el) => {
      if (el.payerName === item.payerName) {
        el.state.push(state.value);
        if (!el.isActive) {
          el.isActive = true;
        }
      }
    });

    //update is an array containing only edited objects.
    setUpdate([...update, item]);
  };

When handleClick is fired and I console the data value in the parent component it has been updated. And I dont want to change that data until the user clicks save. How can I have a copy of the data exclusive to the 2nd component that doesn't manipulate the data in the Parent?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

我偏爱纯白色 2025-01-28 09:59:51

问题

在这里问题是代码正在突变状态对象。

const [update, setUpdate] = useState([]);
const [List, setList] = useState([]);

// sets the data onLoad
useEffect(() => {
  setList(data); // <-- (1) parent state passed as prop
}, []);

// edits the data
const handleClick = (item) => {
  let list = List; // <--(2) reference to state in parent

  list.forEach((el) => {
    if (el.payerName === item.payerName) {
      el.state.push(state.value); // <-- (3) mutation of parent state!
      if (!el.isActive) {
        el.isActive = true; // <-- (3) mutation of parent state!
      }
    }
  });

  // update is an array containing only edited objects.
  setUpdate([...update, item]);
};

解决方案

使用/应用不变的更新Patten。任何正在更新的状态都应将其浅入新数组/对象参考。扩展语法是通过引用的浅副本,这就是为什么创建 new array/object参考对于避免突变原始的原因。

我假设这个子组件只需要/需要维护自己的数据 prop的副本。

const [list, setList] = useState(data); // <-- (1) parent state passed as prop

// edits the data
const handleClick = (item) => {
  setList(list => list.map(el => { // <-- (2) array.map shallow copy
    if (el.payerName === item.payerName) {
      return {
        ...el, // <-- (3) shallow copy array element
        state: [...el.state, state.value], // <-- (4) shallow copy array
        isActive: !el.isActive, // <-- new property
      };
    }
    return el; // <-- not updating, just return current element
  }));
};

Issue

The issue here is that the code is mutating the state objects.

const [update, setUpdate] = useState([]);
const [List, setList] = useState([]);

// sets the data onLoad
useEffect(() => {
  setList(data); // <-- (1) parent state passed as prop
}, []);

// edits the data
const handleClick = (item) => {
  let list = List; // <--(2) reference to state in parent

  list.forEach((el) => {
    if (el.payerName === item.payerName) {
      el.state.push(state.value); // <-- (3) mutation of parent state!
      if (!el.isActive) {
        el.isActive = true; // <-- (3) mutation of parent state!
      }
    }
  });

  // update is an array containing only edited objects.
  setUpdate([...update, item]);
};

Solution

Use/apply the immutable update patten. Any state that is being updated should be shallow copied into to new array/object reference. The spread syntax is a shallow copy by reference, this is why creating new array/object references is necessary to keep from mutating the original.

I'll assume that this child component just wants/needs to maintain its own copy of the data prop.

const [list, setList] = useState(data); // <-- (1) parent state passed as prop

// edits the data
const handleClick = (item) => {
  setList(list => list.map(el => { // <-- (2) array.map shallow copy
    if (el.payerName === item.payerName) {
      return {
        ...el, // <-- (3) shallow copy array element
        state: [...el.state, state.value], // <-- (4) shallow copy array
        isActive: !el.isActive, // <-- new property
      };
    }
    return el; // <-- not updating, just return current element
  }));
};
最舍不得你 2025-01-28 09:59:51

您可以尝试使用传播操作员复制列表。在子组件上,您可以执行类似的操作:

function EditInsurance(props) {
  const [parentData, setParentData] = useState([...props.data]);
  ...
}

然后在子组件上使用parentdata,甚至不需要使用效果。

You can try to copy the list using a spread operator. On the child component, you can do something like:

function EditInsurance(props) {
  const [parentData, setParentData] = useState([...props.data]);
  ...
}

And then use the parentData on your child component, you don't even need the useEffect.

烟柳画桥 2025-01-28 09:59:51

您需要深层克隆才能保存

const [update, setUpdate] = useState([]);
const [list, setList] = useState([]);

useEffect(() => {
  const cloned = data.map(item => {...item})   // clone list and items
  setList(cloned);
}, []);

//edits the data
const handleClick = (item) => {
  list.forEach((el) => {
    if (el.payerName === item.payerName) {
      el.state.push(state.value);
      if (!el.isActive) {
        el.isActive = true;
      }
    }
  });

 setUpdate([...update, item]);
}

handleclick()的单个对象,可能会缩短到

const handleClick = (item) => {
  const payer = list.find(el => el.payerName === item.payerName);
  payer.state.push(state.value);
  payer.isActive = true;

 setUpdate([...update, item]);
}

You'll need deep cloning to preserve individual objects in parent

const [update, setUpdate] = useState([]);
const [list, setList] = useState([]);

useEffect(() => {
  const cloned = data.map(item => {...item})   // clone list and items
  setList(cloned);
}, []);

//edits the data
const handleClick = (item) => {
  list.forEach((el) => {
    if (el.payerName === item.payerName) {
      el.state.push(state.value);
      if (!el.isActive) {
        el.isActive = true;
      }
    }
  });

 setUpdate([...update, item]);
}

Your handleClick() can probably be shortened to

const handleClick = (item) => {
  const payer = list.find(el => el.payerName === item.payerName);
  payer.state.push(state.value);
  payer.isActive = true;

 setUpdate([...update, item]);
}
你的往事 2025-01-28 09:59:51

我不是很确定,但是您可以尝试类似的东西 -

const [List, setList] = useState([]);
const [newList, setNewList] = useState([]);

随着 -

 useEffect(() => {
    setList(data);
  }, []);

使用 -

 useEffect(() => {
    setNewList([...List])
  }, [List]);

这将使您收到的列表道具的克隆副本,现在如果您更改此列表(推送或setstate),则您的父部件将不会反映变化。

现在,在您的Handleclick函数中 - -

    const handleClick = (item) => {
        let MyList = [...newList]

        # this is brcause you are using map. So first push the data in MyList and then setState your newList, so that child component can re-render 
        
        MyList.forEach((el) => {
          if (el.payerName === item.payerName) {
            el.state.push(state.value);
            if (!el.isActive) {
              el.isActive = true;
            }
          }
        });

     # also include this
     setNewList(MyList) 

    setUpdate([...update, item]);

}

在您的子组件渲染方法中使用新清单。

I'm not very sure but you can try something like -

const [List, setList] = useState([]);
const [newList, setNewList] = useState([]);

and along with -

 useEffect(() => {
    setList(data);
  }, []);

use -

 useEffect(() => {
    setNewList([...List])
  }, [List]);

This will make a cloned copy of the List props that you received, and now if you change this (push or setState), your parent component will not reflect the changes.

Now in your handleClick function, -

    const handleClick = (item) => {
        let MyList = [...newList]

        # this is brcause you are using map. So first push the data in MyList and then setState your newList, so that child component can re-render 
        
        MyList.forEach((el) => {
          if (el.payerName === item.payerName) {
            el.state.push(state.value);
            if (!el.isActive) {
              el.isActive = true;
            }
          }
        });

     # also include this
     setNewList(MyList) 

    setUpdate([...update, item]);

}

and use newList everywhere in your child component render method.

尸血腥色 2025-01-28 09:59:51

只需摆脱对props.data的

function EditInsurance(props) {
  const [parentData, setParentData] = useState([JSON.parse(JSON.stringify(props.data))]);
  ...
}

                         OR

参考

function EditInsurance(props) {
  const [parentData, setParentData] = useState([._cloneDeep(props.data))]);
  ...
}

just get rid of the reference to props.data like so

function EditInsurance(props) {
  const [parentData, setParentData] = useState([JSON.parse(JSON.stringify(props.data))]);
  ...
}

                         OR

use lodash's clonedeep/clone method

function EditInsurance(props) {
  const [parentData, setParentData] = useState([._cloneDeep(props.data))]);
  ...
}
临风闻羌笛 2025-01-28 09:59:51

因此,问题是为什么从子女组件中更改数据的数据?的数据,JS中的对象是参考,
当您执行这样的事情时,

//sets the data onLoad
useEffect(() => {
   setList(data);
}, []);

父母的列表和父母的数据是指内存中的同一对象,更改列表中的某些内容将直接反映到数据。好练习。现在,您可以通过在儿童组件中创建新的数据副本来使工作是工作的,

//sets the data onLoad
useEffect(() => {
   setList({...data});
}, []);

但是随着传播操作员的内在数据,将与儿童组件共享,因此更好的方法是使用JSON.STRINGIFY和JSON.PARSE进行深层副本

//sets the data onLoad
useEffect(() => {
   setList(JSON.parse(JSON.stringify(data)));
}, []);

。孩子和父母组件都将拥有自己的数据副本,而儿童的更改将不会反映到父母的成分,希望这对您有所帮助。

so the question is why changing data from child component changes data of parent component?,the reason is objects in js are reference,
when you do something like this,

//sets the data onLoad
useEffect(() => {
   setList(data);
}, []);

both list of child and data of parent refers to same object in memory,changing something in list will directly reflected to data.and again you are trying to change state of parent component from child directly which is not a good practice. now you can make is work by creating new copy of data in child component, like this

//sets the data onLoad
useEffect(() => {
   setList({...data});
}, []);

but with spread operator inner nodes of data again will be shared with child component so better way is to make deep copy using JSON.stringify and JSON.parse

//sets the data onLoad
useEffect(() => {
   setList(JSON.parse(JSON.stringify(data)));
}, []);

this way both child and parent component will have their own copy of data and changes from child will not be reflected to parent component,hope this will help you.

温暖的光 2025-01-28 09:59:51

查看其他答案,并且看到您还没有找到解决方案,您是否尝试过克隆data在将其传递给Prop之前?

父组件:

const [data, setData] = useState([]);
const [editing, setEditing] = useState(null);

{!!editing && (
        <EditInsurance state={editing} data={[...data]} setEditing={setEditing} />
)}

Looking at the other answers, and seeing as you haven't figured out a solution yet, have you tried cloning data before passing it down as a prop?

Parent Component:

const [data, setData] = useState([]);
const [editing, setEditing] = useState(null);

{!!editing && (
        <EditInsurance state={editing} data={[...data]} setEditing={setEditing} />
)}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文