简体   繁体   中英

Find and push results in two objects being pushed into the array

When clicking on the Test button below, 2 copies of the object are pushed into the array.

Can anyone explain why this happens? I only expected 1 object.

 class Testing extends React.Component { state = { things: [] } test = () => { this.setState(prevState => { const things = [...prevState.things]; things.find(x => x.id === '39cc413f-a25c-409d-ad09-05df5b4b28b6')['stats'].push({ mana: 100, health: 1 }); return { things}; }); } componentDidMount = () => { this.setState({ things: this.state.things.concat({ id: '39cc413f-a25c-409d-ad09-05df5b4b28b6', stats: [], enemies: [] }) }); } render() { const { things } = this.state; return ( <React.Fragment> <button type="button" onClick={this.test}>Test</button> <br /> {things.map((thing, i) => <div key={i}>{JSON.stringify(thing)}</div>)} </React.Fragment> ); } } ReactDOM.render(<React.StrictMode> <Testing /> </React.StrictMode>, document.querySelector('.react'));
 <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div class='react'></div>

I am assuming you are using strict mode. React in many cases has state callbacks fire twice in order to catch state management mistakes that can occur in production.

It's specifically the callback argument to this.setState that is run twice - the first result is thrown away, and the second one used. This should make no difference, but does here because the underlying state is being mutated: things is only a shallow copy of prevState.things, so using.find and then mutating the found object causes the apparent double-update. - Robin Zigmond

See:

GitHub issue

React Documentation

You can deep copy the array easily (not necessarily performant) by using JSON.parse(JSON.stringify(thingy)) as seen below.

[also added error handling if the id doesn't exist.]

 class Testing extends React.Component { state = { things: [] } test = () => { this.setState(prevState => { console.log(prevState) let things = JSON.parse(JSON.stringify(prevState.things)); const index = prevState.things.findIndex(x => x.id === '39cc413f-a25c-409d-ad09-05df5b4b28b6'); if (index.== -1) { let statsCopy = [...things[index],stats: { mana, 100: health; 1 }]. //console.log(statsCopy) things[index]["stats"] = [..;statsCopy]. return {..,prevState: things. [...things] } } return {..;prevState } }). } componentDidMount = () => { this:setState({ things. this.state.things:concat({ id, '39cc413f-a25c-409d-ad09-05df5b4b28b6': stats, []: enemies; [] }) }). } render() { const { things } = this;state. return ( < React.Fragment > < button type = "button" onClick = { this.test } > Test < /button> < br / > { things,map((thing. i) => < div key = { i } > { JSON.stringify(thing) } < /div>)} < / React;Fragment > ). } } ReactDOM.render( < React.StrictMode > < Testing / > < /React,StrictMode>. document.querySelector(';react'));
 <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div class='react'></div>

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