简体   繁体   English

为什么我的 globalState state object 在我自己更新之前被更新?

[英]Why is my globalState state object being updated before I update it myself?

I have a multitabbed view that I am controlling the data with through a global state, being passed through useContext (along with the setState updater function).我有一个多选项卡视图,我通过全局 state 控制数据,并通过 useContext(以及 setState 更新程序函数)传递。

The structure is similar to结构类似于

globalState: {
    company: {
        list: [
            [{name: ..., ...}, {field1: ..., ... }],
            [{name: ..., ...}, {field1: ..., ... }],
            ...
        ]
    },
    ...
}

I have a table in this first tab, where each row that displays the details in the first object of each inner list array ( globalState.company.list[X][0] ), and has a few checkboxes to toggle fields in the second object in each inner list array ( globalState.company.list[X][1] ).我在第一个选项卡中有一个表格,其中每一行显示每个内部列表数组( globalState.company.list[X][0] )的第一个 object 中的详细信息,并且有几个复选框来切换第二个中的字段每个内部列表数组 ( globalState.company.list[X][1] ) 中的 object。

The issue I am having is that when I check a checkbox for a specific field, all companies have that field set to that value before I call setGlobalState(...) in that onChange call from the checkbox itself.我遇到的问题是,当我检查特定字段的复选框时,所有公司都将该字段设置为该值,然后我在复选框本身的onChange调用中调用setGlobalState(...)

Here is all the related code for the flow of creating the checkbox and the handler:以下是创建复选框和处理程序流程的所有相关代码:

<td><Checkbox
    disabled={tpr.disabled} // true or false
    checked={tpr.checked} // true or false
    onChange={checkboxOnChange} // function handler
    targetId={company.id} // number
    field={"field1"} />
</td>

Checkbox definition复选框定义

const Checkbox = React.memo(({ disabled, checked, onChange, targetId, field }) => {
    return (
        <input
            type="checkbox"
            style={ /* omitted */ }
            defaultChecked={checked}
            disabled={disabled}
            onChange={(e) => onChange(e, targetId, field)}
        />
    );
});

onChange Handler callback onChange 处理程序回调

const checkboxOnChange = (e, id, field) => {
    const checked = e.target.checked;

    console.log("GLOBAL STATE PRE", globalState.companies.list);

    let foundCompany = globalState.companies.list.find(company => company[0].id === id);
    foundCompany[1][field].checked = checked;
    console.log("foundCompany", foundCompany);

    console.log("GLOBAL STATE POST", globalState.companies.list);

    setGlobalState(prevState => ({
        ...prevState,
        companies: {
            ...prevState.companies,
            list: prevState.companies.list.map(company => {
                console.log("company PRE ANYTHING", company);
                if (company[0].id === foundCompany[0].id) {
                    console.log("Inside here");
                    return foundCompany;
                }
                console.log("company", company);
                return company;
            })
        }
    }));
};

I see from the GLOBAL STATE PRE log that if I were to check a box for field1, then all companies would have field1 checked well before I modify anything else.我从GLOBAL STATE PRE日志中看到,如果我要选中 field1 的框,那么在我修改其他任何内容之前,所有公司都会先选中 field1。 I can confirm that before the box is checked, the globalState is as I expect it to be with all of the data and fields correctly set on load.我可以确认,在选中该框之前,globalState 与我期望的一样,所有数据和字段在加载时都正确设置。

In the picture below, I checked the box for TPR in the second company array, and before anything else happens, the second and third companies already have the TPR set to true.在下图中,我在第二家公司数组中选中了 TPR 框,在发生任何其他事情之前,第二家和第三家公司已经将 TPR 设置为 true。

显示 GLOBAL STATE PRE 日志消息的浏览器控制台

Any help would be appreciated, and I will share any more details I am able to share.任何帮助将不胜感激,我将分享我能够分享的更多细节。 Thank you.谢谢你。

Just don't mutate the state object directly:只是不要直接改变 state object :

const checkboxOnChange = (e, id, field) => {
    const checked = e.target.checked;
    setGlobalState(prevState => ({
        ...prevState,
        companies: {
            ...prevState.companies,
            list: prevState.companies.list.map(company => {
                if (company[0].id === id) {
                    return {
                        ...company,
                        checked
                    };
                }
                return {
                    ...company
                };
            })
        }
    }));
};  

The globalState object is being updated before you call setGlobalState because you are mutating the current state (eg foundCompany[1][field].checked = checked; ) globalState object 在您调用 setGlobalState 之前正在更新,因为您正在改变当前的 state(例如foundCompany[1][field].checked = checked;

One way of getting around this issue is to make a copy of the state object so that it does not refer to the current state.解决此问题的一种方法是复制 state object,使其不引用当前的 state。 eg例如

var cloneDeep = require('lodash.clonedeep');

...

let clonedGlobalState = cloneDeep(globalState);
let foundCompany = clonedGlobalState.companies.list.find(company => company[0].id === id);
foundCompany[1][field].checked = checked;

I recommend using a deep clone function like Lodash's cloneDeep as using the spread operator to create a copy in your instance will create a shallow copy of the objects within your list array.我建议使用像Lodash 的 cloneDeep这样的深度克隆 function,因为使用扩展运算符在您的实例中创建副本将在列表数组中创建对象的浅表副本。

Once you have cloned the state you can safely update it to your new desired state (ie without worry of mutating the existing globalState object) and then refer to it when calling setGlobalState.克隆 state 后,您可以安全地将其更新为所需的新 state(即无需担心改变现有的 globalState 对象),然后在调用 setGlobalState 时引用它。

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

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