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
相关产品推荐
相关产品推荐

