最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Using setState to change multiple values within an array of objects -- ReactJS - Stack Overflow

programmeradmin6浏览0评论

I am new to React and wanted to write a fun project to get a hang of the language. I wanted to write a bubble blowing app, like so I wrote the following JSFiddle:

I am rendering an array of bubble objects stored in the following manner:

history =
 [ { //first element is empty placeholder
    diameter: 0,
    x: 0,
    y: 0
  }, 
  { 
    diameter: 40,
    x: 158,
    y: 122
  },
  { 
    diameter: 86,
    x: 274,
    y: 214
  },
  { 
    diameter: 26,
    x: 158,
    y: 244
  },
]

This exact array will produce the following result:

Now, after mocking this up, I got the crazy idea to add some gravity and bouncing. I wanted the bubbles to bounce like in this JSFiddle:

However, the only way I got the moving bubbles to work was by storing the information directly in state, as in the code below. (vy is the vertical speed of the ball.)

class Box extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        diameter: 0,
        x: 0,
        y: 0,
        vy: 0,
        clicked: false,
    };
}

I use the following code to update the state of box in order to simulate the bouncing of the bubble:

fall(i){
    this.setState({
        y: this.state.y + i,
        vy: this.state.vy + .01,
    });
}

fallWrap(){
    this.fall(this.state.vy);
    this.u = setTimeout(this.fallWrap.bind(this), 1);



    if(this.state.y >= 400 - (this.state.diameter/2)){
        this.setState({
            y: 400 - (this.state.diameter/2),
            vy: -.9 * this.state.vy,
        }, function(){
            if(Math.abs(this.state.vy) < .2){
                clearTimeout(this.u);
            }
        });
    }

}

What I want to do is to store an array of multiple bubbles, like in the first JSfiddle, and have all the bubbles bounce around in the box like the second JS fiddle. This means updating value vy of multiple objects in the history array concurrently. I hope I have adequately described my needs. My central question is how can I use setstate to update a single value (vy) within each object stored within an array? For example, if I have

history =
 [ { 
    diameter: 0,
    x: 0,
    y: 0,
    vy: 0
  }, 
  { 
    diameter: 40,
    x: 158,
    y: 122,
    vy: .5 //update this
  },
  { 
    diameter: 86,
    x: 274,
    y: 214,
    vy: .4 //update this
  },
  { 
    diameter: 26,
    x: 158,
    y: 244
    vy: .23 //update this
  },
]

Then how I implement setState in a way that updates each vy? I know in React, state arrays should be immutable, so I am wondering if there is an established way to do this. The map function seems promising, but I don't know how to apply it here.

EDIT: Thanks everyone, I ended up solving my problem with Haken's solution. Here is a link to the JSFiddle

I am new to React and wanted to write a fun project to get a hang of the language. I wanted to write a bubble blowing app, like so I wrote the following JSFiddle:

I am rendering an array of bubble objects stored in the following manner:

history =
 [ { //first element is empty placeholder
    diameter: 0,
    x: 0,
    y: 0
  }, 
  { 
    diameter: 40,
    x: 158,
    y: 122
  },
  { 
    diameter: 86,
    x: 274,
    y: 214
  },
  { 
    diameter: 26,
    x: 158,
    y: 244
  },
]

This exact array will produce the following result:

Now, after mocking this up, I got the crazy idea to add some gravity and bouncing. I wanted the bubbles to bounce like in this JSFiddle:

However, the only way I got the moving bubbles to work was by storing the information directly in state, as in the code below. (vy is the vertical speed of the ball.)

class Box extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        diameter: 0,
        x: 0,
        y: 0,
        vy: 0,
        clicked: false,
    };
}

I use the following code to update the state of box in order to simulate the bouncing of the bubble:

fall(i){
    this.setState({
        y: this.state.y + i,
        vy: this.state.vy + .01,
    });
}

fallWrap(){
    this.fall(this.state.vy);
    this.u = setTimeout(this.fallWrap.bind(this), 1);



    if(this.state.y >= 400 - (this.state.diameter/2)){
        this.setState({
            y: 400 - (this.state.diameter/2),
            vy: -.9 * this.state.vy,
        }, function(){
            if(Math.abs(this.state.vy) < .2){
                clearTimeout(this.u);
            }
        });
    }

}

What I want to do is to store an array of multiple bubbles, like in the first JSfiddle, and have all the bubbles bounce around in the box like the second JS fiddle. This means updating value vy of multiple objects in the history array concurrently. I hope I have adequately described my needs. My central question is how can I use setstate to update a single value (vy) within each object stored within an array? For example, if I have

history =
 [ { 
    diameter: 0,
    x: 0,
    y: 0,
    vy: 0
  }, 
  { 
    diameter: 40,
    x: 158,
    y: 122,
    vy: .5 //update this
  },
  { 
    diameter: 86,
    x: 274,
    y: 214,
    vy: .4 //update this
  },
  { 
    diameter: 26,
    x: 158,
    y: 244
    vy: .23 //update this
  },
]

Then how I implement setState in a way that updates each vy? I know in React, state arrays should be immutable, so I am wondering if there is an established way to do this. The map function seems promising, but I don't know how to apply it here.

EDIT: Thanks everyone, I ended up solving my problem with Haken's solution. Here is a link to the JSFiddle

Share Improve this question edited Oct 16, 2018 at 23:38 jahneff asked Oct 16, 2018 at 21:04 jahneffjahneff 731 silver badge5 bronze badges 3
  • You don't; you get the current state, you modify it, you set it back to the state. You could update any particular bubble and slice everything back together as you go, but I wouldn't bother. – Dave Newton Commented Oct 16, 2018 at 21:21
  • I don't mind updating them all at the same time. For instance making a copy and modifying all the vy values and then setting it as the state. Hmm maybe that would work. – jahneff Commented Oct 16, 2018 at 21:26
  • You don't need to make an explicit copy, e.g., see Håken's answer. – Dave Newton Commented Oct 16, 2018 at 21:29
Add a ment  | 

3 Answers 3

Reset to default 3

You can pass an updater function to setState. Here's an example of how this might work.

The object returned from the updater function will be merged into the previous state.

const updateBubble = ({y, vy, ...props}) => ({y: y + vy, vy: vy + 0.1, ...props})

this.setState(state => ({bubbles: state.bubbles.map(updateBubble)}))

Change the updateBubble function to add bouncing and so on.

You can do this with map indeed. Here's how I would have done it.

One liner

this.setState({bubbles: this.state.bubbles.map(x => ({...x, vy: x.vy + 1})})

More explicit

this.state = {bubbles: history}

this.setState({ bubbles: this.state.bubbles.map(x => { 

    // Option 1 - more verbose
    let newBubble = x; // or even let newBubble = {...x}
    newBubble.vy = x.vy + 1;

    // Option 2 - directly update x
    x.vy = x.vy + 1

    // Then
    return x;
})})

I would sugest you should change your approach. You should only manage the state as the first you showed, and then, in another ponent, manage multiple times the ponent you currently have.

You could use something like this:

import React from 'react'
import Box from './box'

export default class Boxes extends React.Component {
    constructor(props) {
        super(props);
        this.state = { 
            started:[false,false,false,false] /*these flags will be changed on your "onClick" event*/
        } 
    }
    render() {
        const {started} = this.state
        return(
            <div>
              {started[0] && <Box />}
              {started[1] && <Box />}
              {started[2] && <Box />}
              {started[3] && <Box />}
            </div>
        )
    }
}
发布评论

评论列表(0)

  1. 暂无评论