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

React Hook调用顺序错误问题及API调用修正方案咨询

解决React Hook调用顺序错误问题

错误信息

React has detected a change in the order of Hooks called by SideBarItemTemplateWithData. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks

  Previous render            Next render
  ------------------------------------------------------
  1. useContext                 useContext
  2. useMemo                    useState

问题根源

原代码在条件判断if (room && room.priorityId)内部调用useEndpoint,违反了React Hook的核心规则:所有Hook必须在组件顶层作用域调用,不能放在条件、循环、嵌套函数中。这种写法会导致组件每次渲染时Hook调用顺序不一致,触发上述错误。

原组件代码

function SideBarItemTemplateWithData({ room }: RoomListRowProps): ReactElement {
   const { sidebar } = useLayout();
    let priority: any;
    let userRoomId: any;
    let data: any;
    const href = roomCoordinator.getRouteLink(room.t, room) || '';
    const title = roomCoordinator.getRoomName(room.t, room) || '';
    if (room && room.v && room.v._id) {
       userRoomId = room.v._id;
    }
    if (room && room.priorityId) {
        data = useEndpoint('GET', `/v1/livechat/priorities/${room.priorityId}`);
    }
    if (data && data.value && data.value.name) {
        priority = data.value.name.toLowerCase();
    }

    const {
        lastMessage,
        hideUnreadStatus,
        hideMentionStatus,
        unread = 0,
        alert,
        userMentions,
        groupMentions,
        tunread = [],
        tunreadUser = [],
        rid,
        t: type,
        cl,
    } = room;
    
    return (
        <SideBarItemTemplate
            userId={userRoomId}
            priority={priority}
            is='a'
        />
    );
}

useEndpoint实现及示例

实现代码

export const useEndpoint = <TMethod extends Method, TPath extends PathFor<TMethod>>(
    method: TMethod,
    path: TPath,
): EndpointFunction<TMethod, MatchPathPattern<TPath>> => {
    const { callEndpoint } = useContext(ServerContext);

    return useCallback((params: any) => callEndpoint(method, path, params), [callEndpoint, path, method]);
};

使用示例

const sendEmailCode = useEndpoint('POST', '/v1/users.2fa.sendEmailCode');
await sendEmailCode({ emailOrUsername });

用户尝试的修改(未解决)

const priorityData = useEndpoint('GET', `/v1/livechat/priorities/${room.priorityId}`);
if (room && room.v && room.v._id) {
    userRoomId = room.v._id;
}

const onClickResendCode = async (): Promise<any> => {
    try {
        let priorityVal = '';
        let data = await priorityData();
        if (data && data.name) {
            priorityVal = data.name.toLowerCase();
        }
        return priorityVal;
        
    } catch (error) {
        throw error;
    }
};

priority = onClickResendCode();

正确解决方案

核心思路:将useEndpoint移到组件顶层,用useState管理异步请求结果,用useEffect触发异步请求并更新状态。

修改后的完整代码:

import { useState, useEffect } from 'react';

function SideBarItemTemplateWithData({ room }: RoomListRowProps): ReactElement {
   const { sidebar } = useLayout();
    const [priority, setPriority] = useState<string>('');
    let userRoomId: any;
    const href = roomCoordinator.getRouteLink(room.t, room) || '';
    const title = roomCoordinator.getRoomName(room.t, room) || '';

    // 1. 将useEndpoint移到组件顶层,保证Hook调用顺序稳定
    const getPriorityData = useEndpoint('GET', `/v1/livechat/priorities/${room.priorityId}`);

    if (room && room.v && room.v._id) {
       userRoomId = room.v._id;
    }

    // 2. 用useEffect触发异步请求,依赖room.priorityId和getPriorityData
    useEffect(() => {
        // 没有priorityId时清空状态
        if (!room?.priorityId) {
            setPriority('');
            return;
        }

        // 调用接口获取优先级数据
        const fetchPriority = async () => {
            try {
                const data = await getPriorityData();
                if (data?.name) {
                    setPriority(data.name.toLowerCase());
                } else {
                    setPriority('');
                }
            } catch (error) {
                console.error('获取优先级数据失败:', error);
                setPriority('');
            }
        };

        fetchPriority();
    }, [room.priorityId, getPriorityData]);

    const {
        lastMessage,
        hideUnreadStatus,
        hideMentionStatus,
        unread = 0,
        alert,
        userMentions,
        groupMentions,
        tunread = [],
        tunreadUser = [],
        rid,
        t: type,
        cl,
    } = room;
    
    return (
        <SideBarItemTemplate
            userId={userRoomId}
            priority={priority}
            is='a'
        />
    );
}

关键说明

  • Hook顶层调用:useEndpoint放在组件最外层,确保每次渲染时Hook调用顺序完全一致,符合React规则。
  • 状态管理:用useState保存priority值,异步请求完成后更新状态,触发组件重新渲染展示最新数据。
  • 依赖触发:useEffect的依赖数组包含room.priorityId和getPriorityData,当优先级ID变化或接口函数更新时,自动重新请求数据。
  • 异常处理:添加错误捕获,避免请求失败导致组件崩溃,同时清空状态保证界面一致性。

内容的提问来源于stack exchange,提问作者Lakshmi Praneeth

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 13:55:28