You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React-Router中useParams与useNavigate导致组件不必要重渲染的原因及性能影响咨询

React-Router中useParams与useNavigate导致组件不必要重渲染的原因及性能影响咨询

大家好,我遇到了一个组件重渲染的问题,想请各位帮忙分析下原因:每次点击同一个链接时,我的组件都会出现不必要的重渲染,我怀疑这和使用useParams、useNavigate钩子有关。现在我担心这种情况会影响应用性能,尤其是父组件也跟着重渲染的问题。

父组件代码:

export const Conversation = () => {
    // const { id } = useParams() as { id: string };
    
    const setChat = useChat((state) => state.actions.setChat);
    // const navigate = useNavigate();
    
    const { isLoading, isError, isRefetching, refetch, data } = useQuery(({ signal }) => conversationApi.get('67096041d913474deb2ea5bc', signal), {
        keys: [],
        retry: 5,
        retryDelay: 2000,
        onSelect: ({ messages, ...data }) => data,
        onSuccess: ({ messages }) => {
            setChat({
                messages,
                params: {
                    id: '67096041d913474deb2ea5bc',
                    type: SourceRefPath.CONVERSATION,
                    query: { recipientId: '67096041d913474deb2ea5bc', session_id: useSocket.getState().session_id }
                }
            });
        },
    });

    if (isLoading) return <ChatSkeleton />;

    if (isError) {
        return (
            <OutletError
                title='Something went wrong'
                description='Cannot load conversation'
                callToAction={
                    <Button onClick={refetch} className='mt-5' disabled={isRefetching}>
                        {isRefetching ? <Loader2 className='w-6 h-6 animate-spin' /> : 'try again'}
                    </Button>
                }
            />
        );
    }

    return (
        // <ConversationProvider conversation={data}>
            <Content />
        // </ConversationProvider>
    );
};

Content组件代码:

export const Content = () => {
    // const { _id, isInitiatorBlocked, isRecipientBlocked, recipient, isRecipientTyping, handleTypingStatus } = useConversation(useShallow(contentSelector));

    const showDetails = useChat((state) => state.showDetails);
    // const description = getConversationDescription({ data: { recipient, isInitiatorBlocked, isRecipientBlocked }, isRecipientTyping });
console.log('rerender')
    return (
        <OutletContainer>
            test
            {/* <div className='flex-1 flex flex-col'>
                <OutletHeader
                    name={recipient.name}
                    isOfficial={recipient.isOfficial}
                    description={description}
                    dropdownMenu={<ConversationDDM />}
                />
                <MessagesList getPreviousMessages={(id, cursor) => conversationApi.getPreviousMessages(id, cursor)} />
                <SendMessage
                    handleTypingStatus={handleTypingStatus()}
                    restrictMessaging={[
                        {
                            reason: !!(isInitiatorBlocked || isRecipientBlocked),
                            message: isRecipientBlocked ? `You blocked ${recipient.name}` : `${recipient.name} has restricted incoming messages`
                        },
                        {
                            reason: !_id && recipient.isPrivate,
                            message: `${recipient.name} does not accept new messages`
                        }
                    ]}
                />
            </div>
            {showDetails && (
                <OutletDetails
                    title='User Info'
                    name={recipient.name}
                    avatarUrl={recipient.avatar?.url}
                    description={description}
                    info={[
                        { data: recipient.status, type: OutletDetailsTypes.BIO },
                        { data: recipient.login, type: OutletDetailsTypes.LOGIN },
                    ]}
                />
            )} */}
        </OutletContainer>
    );
};

用于导航的NavLink组件代码:

export const ConversationItem = ({ feedItem: { item } }: { feedItem: ExctactLocalFeedItem<FeedTypes.CONVERSATION> }) => {
    const recipient = item.recipient;
    const draft = useLayout((state) => state.drafts).get(recipient._id);

    return (
        <li>
            <NavLink
                to={`/conversation/${recipient._id}`}
                className={({ isActive }) =>
                    cn(
                        'flex items-center gap-5 p-2 rounded-lg transition-colors duration-200 ease-in-out',
                        isActive
                            ? 'dark:bg-primary-dark-50 bg-primary-gray/10'
                            : 'dark:hover:bg-primary-dark-50/30 hover:bg-primary-gray/5'
                    )
                }
            >
                {recipient.avatar ? (
                    <span className='relative w-[50px] h-[50px]'>
                        <Image
                            src={recipient.avatar.url}
                            skeleton={<AvatarByName name={recipient.name} size='lg' />}
                            className='object-cover object-center min-w-[50px] max-w-[50px] h-[50px] rounded-full'
                        />
                        {recipient.presence === PRESENCE.ONLINE && <ProfileIndicator />}
                    </span>
                ) : (
                    <AvatarByName name={recipient.name} size='lg' isOnline={recipient.presence === PRESENCE.ONLINE} />
                )}
                <div className='flex flex-col items-start w-full overflow-hidden'>
                    <Typography
                        as='h2'
                        weight='medium'
                        className='flex w-full items-center'
                    >
                        {recipient.name}
                        {recipient.isOfficial && (
                            <Typography className='ml-2'>
                                <Verified className='w-5 h-5' />
                            </Typography>
                        )}
                        {!!item.unreadMessages && (
                            <span className='text-primary-dark-200 font-semibold text-sm flex items-center justify-center ml-auto size-5 rounded-full bg-primary-white'>
                                {item.unreadMessages}
                            </span>
                        )}
                    </Typography>
                    {!!item.participantsTyping?.length ? (
                        <Typography as='p' variant='secondary' className='line-clamp-1'>
                            typing...
                        </Typography>
                    ) : draft?.state === 'send' ? (
                        <Typography as='p' variant='secondary' className='line-clamp-1 break-all'>
                            <Typography as='span' variant='error'>
                                Draft:&nbsp;
                            </Typography>
                            {draft.value}
                        </Typography>
                    ) : (
                        !!item.lastMessage && (
                            <div className='flex items-center w-full gap-5'>
                                <Typography className='break-all dark:text-primary-white/30 text-primary-gray line-clamp-1'>
                                    {item.lastMessage?.text}
                                </Typography>
                                <Typography className='ml-auto' variant='secondary'>
                                    {new Date(item.lastMessage.createdAt).toLocaleTimeString(navigator.language, {
                                        hour: 'numeric',
                                        minute: 'numeric'
                                    })}
                                </Typography>
                            </div>
                        )
                    )}
                </div>
            </NavLink>
        </li>
    );
};

备注:内容来源于stack exchange,提问作者Flain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 17:19:52