skip to Main Content

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.

2

Answers


  1. 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(() => {
      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])
    
    Login or Signup to reply.
  2. 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) => {
      const chatObject = {
        id: chats.length + 1,
        direction: "sending",
        type: "message",
        message: data.messageToSend,
      };
      setChats((prev) => [...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 (
      <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>
    );
    

    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 }) => {
        /* define component */
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search