sairam
sairam

Reputation: 35

how to setState an array which is nested inside an array of objects

this is my state

this.state = {
    notification: [{
            from: {
                id: someid,
                name: somename
            },
            message: [somemessage]
        },
        {..},
        {..},
    ]
}

Now if i get a new message from someid, i have to push that new message into message array of someid

I tried to push that message in different ways, but nothing has worked

I tried it in this way, but im not able to push a new message into message array

if (this.state.notification) {
    for (let q = 0; q < this.state.notification.length; q++) {
        if (
            this.state.notification[q] &&
            this.state.notification[q].from &&
            this.state.notification[q].from.id === r.from.id
        ) {
            this.setState({
                notification: [
                    ...this.state.notification[q].messages,
                    this.state.notification[q].messages.push(r.message),
                ],
            });
            return console.log(this.state.notification[q].messages)
        }
    }
} else {
    this.setState({
        notification: [{
            from: r.from,
            messages: [r.message]
        }, ]
    });
    return console.log(JSON.stringify(this.state.notification));
}

Upvotes: 2

Views: 75

Answers (3)

Quentin Grisel
Quentin Grisel

Reputation: 4987

I'm pretty sure you can't do this: this.state.notification[q].messages.push(r.message). You can't mutate your state directly. You should work with a copy o your state, modify it with your code that seems to be ok, and then do the setState(...).

Here is a repro on Stackblitz that works. Here is the code :

import React, { Component } from "react";
import { render } from "react-dom";
import "./style.css";

class App extends Component {
  constructor() {
    super();
    this.state = {
      notifications: [
        {
          from: { id: 0, name: "Quentin" },
          message: ["Message 1"]
        },
        {
          from: { id: 1, name: "John" },
          message: ["Message 1"]
        },
        {
          from: { id: 2, name: "Henry" },
          message: ["Message 1"]
        }
      ]
    };

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

  pushMessage (id, message) {
    const newState = Object.assign([], this.state);
    newState.notifications.forEach(notif => {
      if (notif.from.id === id) {
        notif.message.push(message);
      }
    });

    this.setState(newState, () => console.log(this.state));
  };

  render() {
    return (
      <div>
        <button onClick={() => this.pushMessage(1, "Hello World")}>
          Push message
        </button>
      </div>
    );
  }
}

render(<App />, document.getElementById("root"));

I only handle the push of a message in an existing notification, you already got the other case.

Upvotes: 1

Mu-Majid
Mu-Majid

Reputation: 845

The first argument to setState is an updater function that takes the previous state as an argument. I think you should use this fact to update your state correctly.

Check out this answer https://medium.com/@baphemot/understanding-reactjs-setstate-a4640451865b.

Upvotes: 0

Neoflies
Neoflies

Reputation: 293

First of all, I think structuring your state as a 2d array is not a good idea. But you can try this

const pushMessage = (someId, someMessage) => {
  this.setState({
    notifications: this.state.notifications.map((notification) => {
      if (notification.from.id === someId) {
        return {
          ...notification,
          messages: [...notification.messages, someMessage],
        };
      }
      return notification;
    }),
  });
};

Upvotes: 3

Related Questions