简体   繁体   中英

State was mutated as a side effect?

I ran into a weird problem with my code. Sorry if this is already asked, I wanted to search for the reason but I don't even know how to phrase the problem.

Essentially, I have a code similar to this:

class Component extends Component {
constructor(props) {
    super(props);
    this.state = { grid: [["", "", "", ""]], base: ["", "", "", ""]};
}

csvParse = (row, column) => e => {
    const newData = Papa.parse(e.target.value).data.filter(
        x => x.length !== 1 && x[0] !== ""
    );
    //newData will result in a 2D array such as [["1", "2", "3", "4"], ["5", "6", "7", "8"], ["9", "10", "11", "12"]]
    //newData is receiving the data from a copy and paste from a CSV file

    const { base } = this.state;
    let grid = this.state.grid

    for (let i = row; i < newData.length + row; i++) {
        for (let j = column; j < 4; j++) {
            try {
                grid[i][j] = newData[i][j];//1
            } catch (err) {
                grid.push(base);//2
                grid[i][j] = newData[i][j];//3 One of these is causing it, I don't know which
            }
        }
    }

    this.setState({grid},()=>{
        console.log(this.state.base) 
    //Actually logs ["9", "10", "11", "12"] instead of ["", "", "", ""]. Why was it mutated?
    })
};

render() {
    //The component renders a 4 X 1 grid of textareas. The number of rows will increase based on this.state.grid. Doesn't seem like its the issue.
    return (
        <Fragment>
            <Grid container>
                    {this.state.grid.map((row, rowIndex) => (
                        <Fragment key={rowIndex}>
                            <Grid item xs={3}>
                                <textarea
                                    onChange={this.csvParse(rowIndex, 0)}
                                    value={row[0]}
                                />
                            </Grid>
                            <Grid  item xs={3}>
                                <textarea
                                    onChange={this.csvParse(rowIndex, 1)}
                                    value={row[1]}
                                />
                            </Grid>
                            <Grid item xs={3}>
                                <textarea
                                    onChange={this.csvParse(rowIndex, 2)}
                                    value={row[2]}
                                />
                            </Grid>
                            <Grid item xs={3}>
                                <textarea
                                    onChange={this.csvParse(rowIndex, 3)}
                                    value={row[3]}
                                />
                            </Grid>
                        </Fragment>
                    ))}
                </Grid>
        </Fragment>
    );
}

}

Every time I ran it this.state.base would change to something else, like [""]. This code has no problem if I ran it without this.state.base

Ah, I believe you want state.base to remain ['','','',''] . If that's the case read on. Else let me know and we can iterate.

To understand what's changing state.base you need to understand the difference between pass by reference and pass by value.

Pass-by-reference vs. Pass-by-value

Essentially in pass by reference, if you create a variable a , then create a variable b equal to a , then change the value of a key/value in a , you'll also change b :

var a = {'hi': 'mom'}
var b = a;
a['hi'] = 'world';
console.log(b);
// logs {hi: 'world'}

In pass by value, if you create a variable a , then create a variable b equal to a , then change the value of a , you won't change b :

var a = true;
var b = a;
a = false;
console.log(b);
// logs true;

Objects {} and Arrays [] are pass by reference in JavaScript, most other primitive data types are pass by value. This is a strange but critically important facet of the language itself, and it changes the way we need to write React.

How this applies to your case

In your case, you assign your state.base to base , then you add base to grid , then you mutate grid . It's only a matter of time before state.base gets mutated.

To avoid this, just change:

grid.push(base);

to:

grid.push(Object.assign([], base));

Object assign here will create a new variable with the value of base, and add that to grid. That prevents any changes to grid from modifying state.base .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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