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

React中useContext辅助函数调用报错:如何全局设置API数据?

问题:React Context钩子在辅助函数中调用报错

我通过Context API创建了全局空对象变量workspacesData,尝试在辅助函数GetWorkspaces中调用API并将返回数据赋值给该变量。但在MavenlinkPage页面点击按钮调用该函数时,触发错误提示:"Invalid hook call. Hooks can only be called inside of the body of a function component."。我明确问题源于在辅助函数中使用了useContext钩子,但不清楚如何解决,希望实现通过全局变量存储API数据的需求。

相关代码:

getWorkspaces.js

import { useContext } from "react";
import { AppContext } from "../../../context/context";
import mavenlinkAPI from "../apiTools";

const GetWorkspaces = async (mavenlinkAccessToken) => {

    const {setWorkspacesData} = useContext(AppContext);

    try{
        const data = await mavenlinkAPI(
            'get',
            'workspaces?token='+mavenlinkAccessToken,
        );
        console.log(data)
        setWorkspacesData(data);
        
    } catch (error) {
        console.log(error)
    }

}

export default GetWorkspaces;

MavenlinkPage.jsx

import { Container, Grid, Paper } from '@mui/material';
import React, {useContext, useEffect, useRef, useState} from 'react';
import { getAuth } from "firebase/auth";
import Fab from '@mui/material/Fab';
import AddIcon from '@mui/icons-material/Add';
import {SuccessSnackbar, ErrorSnackbar} from '../components/PopupSnackbar';
import { AppContext } from '../context/context';
import GetWorkspaces from '../helpers/api/mavenlink/getWorkspaces';
import GetTimesheets from '../helpers/api/mavenlink/getTimesheets';
import GetUsers from '../helpers/api/mavenlink/getUsers';
import CreateProject from '../helpers/api/mavenlink/createProject';
import GetMavenlinkAccessToken from '../helpers/api/mavenlink/getMavenlinkAccessToken';
import DummyDataHolder from '../components/dummyDataHolder';

export const MavenlinkPage = () => {
    const { mavenlinkConnected } = useContext(AppContext);
    const [errorAlert, setErrorAlert] = useState(false);
    const [successAlert, setSuccessAlert] = useState(false);
    const { mavenlinkAccessToken, setMavenlinkAccessToken } = useContext(AppContext);

    const { workspacesData } = useContext(AppContext);


    const auth = getAuth();
    const user = auth.currentUser;
    const uid = user.uid

    const handleAlertClose = (event, reason) => {
        if (reason === 'clickaway') {
          return;
        }
        setSuccessAlert(false) && setErrorAlert(false);
    };

    useEffect(() => {
        if(mavenlinkConnected){
            GetMavenlinkAccessToken(setMavenlinkAccessToken);
        }
       
    }, [])

    console.log(mavenlinkAccessToken)

    return(
        <>
            <Container>
                <div className="mavenlink-page">
                    <Grid container spacing={2}>
                        <Grid item xs={12}>
                            <h1>Mavenlink</h1>
                        </Grid>
                        <Grid item xs={12}>
                            <Paper className="connection-status" elevation={1}>
                                    <h4 className="title">Connection Status:</h4>
                                    {!mavenlinkConnected ? <h4 className="response-error">{user.email} is not connected</h4> : <h4 className="response-success">{user.email} is connected</h4>}         
                            </Paper>
                        </Grid>
                        <Grid item xs={12}>
                            <Paper elevation={1}>
                                <h4>Get Workspaces</h4>
                                <Fab onClick={() => GetWorkspaces(mavenlinkAccessToken)} color="primary" aria-label="add">
                                    <AddIcon />
                                </Fab>
                                <h4>Get timesheets</h4>
                                <Fab onClick={() => GetTimesheets(mavenlinkAccessToken)} color="primary" aria-label="add">
                                    <AddIcon />
                                </Fab>
                                <h4>Get users</h4>
                                <Fab onClick={() => GetUsers(mavenlinkAccessToken)} color="primary" aria-label="add">
                                    <AddIcon />
                                </Fab>
                                <h4>Create project</h4>
                                <Fab onClick={() => CreateProject(mavenlinkAccessToken)} color="primary" aria-label="add">
                                    <AddIcon />
                                </Fab>
                            </Paper>
                        </Grid>
                        <Grid item xs={12}>
                        <Paper elevation={10} style={{ height: 400, width: '100%' }}>
                            {workspacesData != null ? 
                                Object.keys(workspacesData).map((item, index) => {
                                    return(
                                        <div key={index}>
                                            {workspacesData[item].map((m, ind) =>
                                                <div key={ind}>{item}</div>
                                            
                                            )}

                                        </div>
                                
                                    )

                                })
                            :
                                <div></div>
                             }
                        </Paper>
                        </Grid>
                    </Grid>
                </div>
                {successAlert === true ? <SuccessSnackbar open={successAlert} handleClose={handleAlertClose}/> : <> </> }
                {errorAlert === true ? <ErrorSnackbar open={errorAlert} handleClose={handleAlertClose}/> : <> </> }
            </Container>
        </>
    );
};

解决方案

React钩子规则明确要求:钩子只能在函数组件的顶层或自定义钩子内部调用,不能在普通辅助函数中使用。以下是两种可行的解决方式:

方案1:拆分逻辑,让辅助函数专注于API请求

将GetWorkspaces改为纯API请求函数,不依赖Context;在组件内部获取Context的更新函数,调用API后再更新全局状态。

修改后的getWorkspaces.js

import mavenlinkAPI from "../apiTools";

// 仅负责请求数据并返回,不处理状态更新
const GetWorkspaces = async (mavenlinkAccessToken) => {
    try{
        const data = await mavenlinkAPI(
            'get',
            'workspaces?token='+mavenlinkAccessToken,
        );
        console.log(data)
        return data;
        
    } catch (error) {
        console.log(error)
        throw error; // 抛出错误让组件处理
    }
}

export default GetWorkspaces;

修改MavenlinkPage.jsx的点击事件

// 在组件顶层获取Context的更新函数
const { workspacesData, setWorkspacesData } = useContext(AppContext);

// 更新按钮点击逻辑
<Fab onClick={async () => {
  try {
    const data = await GetWorkspaces(mavenlinkAccessToken);
    setWorkspacesData(data);
    setSuccessAlert(true);
  } catch (err) {
    setErrorAlert(true);
  }
}} color="primary" aria-label="add">
  <AddIcon />
</Fab>

方案2:封装为自定义钩子(推荐)

将数据请求与Context更新逻辑封装成自定义钩子,既遵守钩子规则,又能复用逻辑。

创建useWorkspaces.js自定义钩子

import { useContext } from "react";
import { AppContext } from "../../../context/context";
import mavenlinkAPI from "../apiTools";

export const useWorkspaces = () => {
    const { setWorkspacesData } = useContext(AppContext);

    const getWorkspaces = async (mavenlinkAccessToken) => {
        try{
            const data = await mavenlinkAPI(
                'get',
                'workspaces?token='+mavenlinkAccessToken,
            );
            console.log(data)
            setWorkspacesData(data);
            return data;
        } catch (error) {
            console.log(error)
            throw error;
        }
    };

    return { getWorkspaces };
};

在MavenlinkPage.jsx中使用自定义钩子

// 导入自定义钩子
import { useWorkspaces } from '../helpers/api/mavenlink/useWorkspaces';

export const MavenlinkPage = () => {
    // ...其他代码
    const { getWorkspaces } = useWorkspaces();

    // 更新按钮点击逻辑
    <Fab onClick={async () => {
      try {
        await getWorkspaces(mavenlinkAccessToken);
        setSuccessAlert(true);
      } catch (err) {
        setErrorAlert(true);
      }
    }} color="primary" aria-label="add">
      <AddIcon />
    </Fab>
    // ...其他代码
};

内容的提问来源于stack exchange,提问作者Michael Stubbs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 01:39:17