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

如何在React中复用自定义useRosterViewer Hook?

如何在自定义Hook中复用useRosterViewer获取排班数据

问题背景

我正在开发一个排班管理应用,已实现useRosterViewer Hook用于获取并管理排班数据,在排班查看组件中运行正常。现在要构建排班编辑器,希望复用useRosterViewer里的数据获取逻辑,避免重复编写异步请求代码,但尝试两种写法均出现问题:

写法一:初始状态同步失效

export function useRosterEditor(){
    const { error, isLoading, roster } = useRosterViewer();
    const [itemList, updateItemList] = useReducer(reducer, {
        error,
        isLoading,
        roster,
    });
    return {
        error: itemList.error,
        isLoading: itemList.isLoading,
        roster: itemList.roster,     
    }
}

此写法中,useReducer的初始值仅在Hook首次渲染时获取useRosterViewer的初始状态,后续useRosterViewer数据更新后,itemList无法同步更新。

写法二:违反Hook调用规则

export function useRosterEditor(){
    const [itemList, updateItemList] = useReducer(reducer, {
        error:null,
        isLoading:true,
        roster:null,
    });
    useEffect(()=>{
        let getData = async () => {
               const { error, isLoading, roster } = useRosterViewer();
         }
         getData();
        },[]) 
    return {
        error: itemList.error,
        isLoading: itemList.isLoading,
        roster: itemList.roster,     
    }
}

触发React Hook规则错误:React Hook "useRosterViewer" is called in function "getData" that is neither a React function component nor a custom React Hook function,因为Hook只能在组件或自定义Hook的顶层调用,不能在异步函数、循环或条件判断内调用。


正确复用方案

方案一:结合useReducer同步状态

在useRosterEditor顶层调用useRosterViewer,通过useEffect监听数据变化并同步到编辑状态中,同时保留编辑专用的状态管理逻辑:

// 编辑专用reducer,处理排班修改、状态同步等逻辑
const editorReducer = (state, action) => {
    let result = { ...state };
    switch (action.type) {
        case "sync-roster":
            result.roster = action.roster;
            result.error = action.error;
            result.isLoading = action.isLoading;
            break;
        // 添加编辑相关的action,比如修改单条排班
        case "update-shift":
            result.roster = result.roster.map(shift => 
                shift.id === action.id ? { ...shift, ...action.data } : shift
            );
            break;
        default:
            break;
    }
    return result;
};

export function useRosterEditor(){
    // 顶层调用useRosterViewer,符合Hook规则
    const { error, isLoading, roster } = useRosterViewer();
    
    // 用useRosterViewer的初始值初始化编辑状态
    const [itemList, updateItemList] = useReducer(editorReducer, {
        error,
        isLoading,
        roster,
    });

    // 监听源数据变化,同步到编辑状态
    useEffect(() => {
        updateItemList({
            type: "sync-roster",
            roster,
            error,
            isLoading
        });
    }, [error, isLoading, roster]);

    // 暴露编辑方法,比如保存排班
    const saveRoster = async () => {
        try {
            await saveRosterApi(itemList.roster);
            // 保存成功后可触发数据刷新(若需要)
        } catch (err) {
            updateItemList({ type: "set-error", error: err.message });
        }
    };

    return {
        ...itemList,
        saveRoster
    };
}

方案二:简化复用(无复杂状态管理)

如果编辑逻辑简单,不需要额外的状态管理,可直接复用useRosterViewer的返回值,再添加编辑方法:

export function useRosterEditor(){
    const viewerState = useRosterViewer();
    
    const saveRoster = async (updatedRoster) => {
        await saveRosterApi(updatedRoster);
        // 若需要刷新数据,可给useRosterViewer添加refresh方法,或修改其依赖项触发重新请求
    };

    return {
        ...viewerState,
        saveRoster
    };
}

内容的提问来源于stack exchange,提问作者The KNVB

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 19:13:20