camlyport
camlyport

Reputation: 109

React - How to push array in object using useState

I'm building chat app using ReactJS, NestJS, Socket.io .

And it's multi channel using rooms in socket.io

const [messages, setMessages] = useState({
        ReactJS: [],
        NestJS: [],
        Typescript: [],
        MySQL: [],
        Neo4j: [],
        Redis: [],
        ELK: [],
        Docker: [],
        Kubernetes: [],
        AWS: [],
        SocketIO: [],
    });

This is array with useState for pushing message.

Question

messages['ReactJS'].push(someMessage);

How useState push element to array inside object?

Upvotes: 1

Views: 1884

Answers (2)

amirhe
amirhe

Reputation: 2341

If you can install some additional utilities here are some other way to do it

Ramda

rootPath = 'ReactJS'
const newArray = R.append(someMessage, messages[rootPath])
const newMessages = R.assocPath([rootPath], newArray, messages);
setMessages(newMessages)

// combined
const rootPath = 'ReactJS'
setMessages(
    R.assocPath(
        [rootPath],
        R.append(
            someMessage,
            messages[rootPath]
        ),
        messages
    )
)

Immerjs

import produce from 'immer'

const rootPath = 'ReactJS'
const newMessages = produce(messages, draftState => {
    draftState[rootPath].push = someMessage
})

setMessages(newMessages)

// combined
import p from 'immer'

const rootPath = 'ReactJS'
setMessages(p(messages, draft => {
    draft[rootPath].push = someMessage
}))

Upvotes: 0

Drew Reese
Drew Reese

Reputation: 202608

Given the state

const [messages, setMessages] = useState({
  ReactJS: [],
  NestJS: [],
  Typescript: [],
  MySQL: [],
  Neo4j: [],
  Redis: [],
  ELK: [],
  Docker: [],
  Kubernetes: [],
  AWS: [],
  SocketIO: [],
});

Then the following is a way to update a specific room via a roomKey identifier nested in the state object. In React when you update state you must always return a new object reference, and this includes any nested state/properties that are being updated. array.prototype.push mutates the original array, it doesn't create a new array reference for React purposes.

setMessages(messages => ({
  ...messages, // <-- shallow copy state
  // copy existing nested state array into new array and append new element
  [roomKey]: [...messages[roomKey], newMessage],
}));

An alternative to the array literal is to use array.prototype.concat, which does return a new array.

setMessages(messages => ({
  ...messages, // <-- shallow copy state
  // copy existing nested state array into new array and append new element
  [roomKey]: messages[roomKey].concat(newMessage),
}));

Note: This assumes your roomKey variable will reference one of the keys actually defined in your state. If you use an unspecified key then messages[unknownKey] will be undefined. In this case, if you've truly dynamic keys, then you can provide a fallback value to spread into state.

setMessages(messages => ({
  ...messages, // <-- shallow copy state
  // copy existing nested state array into new array and append new element
  [roomKey]: [
    ...messages[roomKey] || [], // <-- provide fallback
    newMessage,
  ],
}));

Upvotes: 3

Related Questions