开发者问题收集

React - 如何使用 useState 将数组推送到对象中

2021-01-16
1867

我正在使用 ReactJS、NestJS、Socket.io 构建聊天应用程序。

它是使用 socket.io 中的房间的多通道

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

这是用于推送消息的带有 useState 的数组。

问题

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

useState 如何使用元素将元素推送到对象内的数组?

2个回答

给定状态

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

然后,以下是通过嵌套在状态对象中的 roomKey 标识符更新特定房间的方法。在 React 中,当您更新状态时,您 必须 始终返回一个新的对象引用,这包括正在更新的任何嵌套状态/属性。 array.prototype.push 会改变原始数组,它不会为 React 目的创建新的数组引用。

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

数组文字的替代方法是使用 array.prototype.concat ,它会返回一个新数组。

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

注意 :这假定您的 roomKey 变量将引用您的状态中实际定义的键之一。如果您使用未指定的密钥,则 messages[unknownKey] 将未定义。在这种情况下,如果您拥有真正的动态密钥,则可以提供后备值以扩展到状态。

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

如果您可以安装一些其他实用程序,这里还有其他方法可以做到

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
}))
amirhe
2021-01-16