英文:
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(() => {
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])
答案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数组映射到MessageItem
或AttachmentItem
组件,具体取决于每个聊天的类型:
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()
来包装您的MessageItem
和AttachmentItem
组件,以便它们仅在其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) => {
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 */
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论