How to use setState to splice into an array in the state?

Viewed 962

My state.events is in array that is made up of the component instance: EventContainer.

I want my setState to place a new EventContainer in the state.events array. However, I want that EventContainer to go in the index immediately after the specific EventContainer that made the setState call.

I'm looking for help with making the adjustments necessary to my approach or, if my entire approach is bad, a recommendation on how to go about this. Thank you very much.

I'm developing an itinerary builder which is made up of rows/EventContainers that represent an activity on a given day.

Each EventContainer has a button that needs to offer the user the ability to onClick an additional row immediately after that EventContainer.

class DayContainer extends React.Component {
  constructor(props){
    super(props);

    this.state = {
      events: [],

    };

    this.pushNewEventContainerToState = this.pushNewEventContainerToState.bind(this);
  }




    pushNewEventContainerToState (index){
       let newEvent = <EventContainer />;
       this.setState(prevState => {
         const events = prevState.events.map((item, j) => {
           if (j === index) {
             events: [...prevState.events.splice(index, 0, newEvent)]
            }
          })
        })
      }




  render(){
    return (
        <>
          <div>
              <ul>
                  {
                    this.state.events === null
                    ? <EventContainer pushNewEventContainerToState= . 
 {this.pushNewEventContainerToState} />
                    : <NewEventButton pushNewEventContainerToState={this.pushNewEventContainerToState} />
                  }




                  {this.state.events.map((item, index) => (
                    <li
                        key={item}
                        onClick={() => 
 this.pushNewEventContainerToState(index)}
                    >{item}</li>
                  ))}

              </ul>

        </div>
      </>
    )
  }
}

My goal in setState was to splice newEvent into this.state.events immediately after the index (the parameter in pushNewEventContainerToState function).

I'm getting this error but I'm guessing there's more going on than just this: Line 23:22: Expected an assignment or function call and instead saw an expression no-unused-expressions.

2 Answers

I can see at least 2 issues with the code. - Splice will mutate the array in place - You are not returning the updated state.

You can instead use slice to build the new array.

pushNewEventContainerToState(index) {
  let newEvent = < EventContainer / > ;

  this.setState(prevState => {
    const updatedEvents = [...prevState.events.slice(0, index], newEvent, ...prevState.events.slice(index + 1];

      return {
        events: updatedEvents
      })
  })
}

As I'm fairly new to coding, it took me awhile but I was able to compile the full answer. Here is the code, below. Below that, I explain, point by point, what the problem was and how the updated code addresses that.

class DayContainer extends React.Component {
  constructor(props){
    super(props);

    this.state = {
      events: [{key:0}],

    };

    this.pushNewEventContainerToState = this.pushNewEventContainerToState.bind(this);
  }


      pushNewEventContainerToState(index) {
        let newEvent = {key: this.state.events.length};

        this.setState(prevState => {
          let updatedEvents = [...prevState.events.slice(0, index + 1), newEvent, ...prevState.events.slice(index + 1)];

            return {
              events: updatedEvents
            };
          })
        }

  render(){
    return (
        <>
          <div>
              <ul>
                  {this.state.events.map((item, index) => (
                    <li key={item.key}>
                        < EventContainer pushNewEventContainerToState={() => this.pushNewEventContainerToState(index) } / >
                    </li>
                  ))}

              </ul>
          </div>
        </>
    )
  }
}

Setup

  1. Starting with state.events, instead of starting with an empty array, I'm starting with one object, including a key starting at 0, because I always want the user to start with one EventContainer.

  2. Regarding pushNewEventContainerToState, @Sushanth made a great recommendation. Please refer directly to that function in my latest code. The refinement I made has to do with the way I separate the EventContainer being passed to this.state.events. I've moved the EventContainer from pushNewEventContainerToState down to the render() element. I've given it a prop of key={item.key} and wrapped the component instance in a li. The very first EventContainer will have a key of 0 (see state.events[0]). Now, each new EventContainer passed to state.events will have a key that's based off the latest .length() of the state.events array (refer to the latest value of the let newEvent variable in pushNewEventContainerToState).

All of that allowed me to fix a big problem I was facing: I needed the newest EventContainer to be placed in the index immediately after the index of the EventContainer calling pushNewEventContainerToState. The main reason this was happening was because I wasn't properly passing the index to the EventContainer inside of render(). Now that I have the actual EventContainer there, I can pass it a prop in the right manner (please refer EventContainer's prop in render). Now I'm calling pushNewEventContainerToState with the correct index.

Related