重新渲染聊天项目列表以适应每条发送的聊天消息是否可以?

huangapple go评论57阅读模式
英文:

Is it ok to re-render list of chat items for every chat message that is sent?

问题

这段代码中,每当 "chats" 更新时,它会触发 useEffect 并将所有 "chats" 映射为 "chatItems" 数组。这种做法会导致性能问题吗?还是说 "list" 的键会处理,只映射已更改或新的组件?我应该这样做还是直接使用 "setChatItems" 来更新我的聊天应用视图?

需要注意的是,目前这段代码正常运行,但我想确保我是按正确的方式操作,以防我的聊天数据变得更大。

谢谢。

英文:

Salam. I have a simple chat app (Not using socket) that triggers useEffect to map all data from "chats" list (which is an array of objects) as MessageItem or AttachmentItem component to an array called "chatItems". Here is the code:

  const [chats, setChats] = useState(list);
  const [chatItems, setChatItems] = useState();

  useEffect(() => {
    setChatItems(
      chats.map((item) =>
        item.type === "message" ? (
          <MessageItem
            key={item.id}
            direction={item.direction}
            message={item.message}
          />
        ) : (
          <AttachmentItem
            key={item.id}
            direction={item.direction}
            thumbnail={item.thumbnail}
            fileName={item.fileName}
          />
        )
      )
    );
  }, [chats]);

And this is the submit function (using react hook form):

  const hookFormSubmit = (data) => {
    const chatObject = {
      id: chats.length + 1,
      direction: "sending",
      type: "message",
      message: data.messageToSend,
    };

    setChats((prev) => [...prev, chatObject]);
  };

As you can see, every time "chats" gets updated, it triggers useEffect and it maps all "chats" to "chatItems". Does it cause performance issues? Or does list key handles it and only maps components that are changed or new? Should I do it this way or directly using "setChatItems" to update my chat app view?

I have to mention that code works properly right now, but the thing is that I wanna make sure that I am doing it the right way so it won't cause problems if my chats data gets larger.
Thank you.

答案1

得分: 1

The chatItems 是根据 chats 状态可以计算出的派生数据。您不需要在 useEffect 钩子中使用 chatItems 状态和调用 setter 函数,这会导致重新渲染。

> 当某些内容可以从现有的 props 或状态中计算出来时,不要将其放入状态中。相反,在渲染过程中计算它。

相反,对于这种情况,useMemo() 是最好的方法。请参阅 you-might-not-need-an-effect#caching-expensive-calculations

const chatItems = useMemo(() => {
  return chats.map((item) =>
        item.type === "message" ? (
          <MessageItem
            key={item.id}
            direction={item.direction}
            message={item.message}
          />
        ) : (
          <AttachmentItem
            key={item.id}
            direction={item.direction}
            thumbnail={item.thumbnail}
            fileName={item.fileName}
          />
        )
      )
}, [chats])
英文:

The chatItems is derived data that can be calculated based on the chats state. You don't need the chatItems state and call setter function in useEffect hook, it will cause a re-render.

> When something can be calculated from the existing props or state, don’t put it in state. Instead, calculate it during rendering.

Instead, useMemo() is the best way for this situation. See you-might-not-need-an-effect#caching-expensive-calculations

const chatItems = useMemo(() =&gt; {
  return chats.map((item) =&gt;
        item.type === &quot;message&quot; ? (
          &lt;MessageItem
            key={item.id}
            direction={item.direction}
            message={item.message}
          /&gt;
        ) : (
          &lt;AttachmentItem
            key={item.id}
            direction={item.direction}
            thumbnail={item.thumbnail}
            fileName={item.fileName}
          /&gt;
        )
      )
}, [chats])

答案2

得分: 1

Sure thing! Here's the translation for the code part you provided:

在状态中存储组件不是推荐的做法,因为它会使您的状态变得更大并且更难管理。

您可以将数据存储在chats状态中,然后在render函数中将它们映射到组件。例如,在您的hookFormSubmit函数中,您可以向chats数组添加一个新的chat对象:

例如:

const hookFormSubmit = (data) => {
  const chatObject = {
    id: chats.length + 1,
    direction: "sending",
    type: "message",
    message: data.messageToSend,
  };
  setChats((prev) => [...prev, chatObject]);
};

然后,在您的return语句中,您可以将chats数组映射到MessageItemAttachmentItem组件,具体取决于每个聊天的类型:

return (
  <div>
    {chats.map((item) =>
      item.type === "message" ? (
        <MessageItem
          key={item.id}
          direction={item.direction}
          message={item.message}
        />
      ) : (
        <AttachmentItem
          key={item.id}
          direction={item.direction}
          thumbnail={item.thumbnail}
          fileName={item.fileName}
        />
      )
    )}
  </div>
);

这样,您可以避免在状态中存储组件,并且不必不必要地使用useEffect。您还可以使您的代码更可读和可维护。

您还可以使用React.memo()来包装您的MessageItemAttachmentItem组件,以便它们仅在其props更改时重新渲染。这可以防止在chats数组更改时组件不必要地重新渲染,但组件的props保持不变。有关更多详细信息,请参阅React Hooks API 参考

例如:

const MessageItem = React.memo(({ direction, message }) => {
    /* 定义组件 */
});

I hope this helps! If you have any more questions or need further assistance, feel free to ask, and I'll do my best to provide playful and helpful answers! 😄

英文:

Storing components in state is not recommended, because it makes your state larger and harder to manage.

You can store the data in the chats state and then map them to components in the render function. For example, in your hookFormSubmit function, you can add a new chat object to the chats array:

For example:

const hookFormSubmit = (data) =&gt; {
  const chatObject = {
    id: chats.length + 1,
    direction: &quot;sending&quot;,
    type: &quot;message&quot;,
    message: data.messageToSend,
  };
  setChats((prev) =&gt; [...prev, chatObject]);
};

Then, in your return statement, you can map the chats array to MessageItem or AttachmentItem components, depending on the type of each chat:

return (
  &lt;div&gt;
    {chats.map((item) =&gt;
      item.type === &quot;message&quot; ? (
        &lt;MessageItem
          key={item.id}
          direction={item.direction}
          message={item.message}
        /&gt;
      ) : (
        &lt;AttachmentItem
          key={item.id}
          direction={item.direction}
          thumbnail={item.thumbnail}
          fileName={item.fileName}
        /&gt;
      )
    )}
  &lt;/div&gt;
);

This way, you avoid storing components in state and using useEffect unnecessarily. You also make your code more readable and maintainable.

You can also use React.memo() to wrap your MessageItem and AttachmentItem components, so that they only re-render when their props change. This can prevent unnecessary re-rendering of the components when the chats array changes, but the props of the components remain the same. Please see React Hooks API Reference for more details.

For example:

const MessageItem = React.memo(({ direction, message }) =&gt; {
    /* define component */
});

huangapple
  • 本文由 发表于 2023年7月17日 10:14:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76701147.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定