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

React中如何重构并导出/导入函数?useEffect抽离是否适合封装为自定义Hook?

方案选择:优先封装为自定义Hook

你的逻辑中用到了useState、useEffect这类React内置Hook,按照React官方规则,只有组件和自定义Hook可以调用内置Hook,普通函数不支持直接调用Hook,因此自定义Hook是唯一合法且更易维护的方案,同时可以完美复用状态、副作用逻辑。


重构步骤

先修复原代码的隐含Bug

你当前的useEffect把competitions设为依赖,同时在回调里直接修改competitions状态数组、调用setCompetitions更新状态,会导致无限触发请求,且直接修改状态不符合React不可变数据原则,重构时会同步修复该问题。

第一步:创建独立自定义Hook文件

新建src/hooks/useCompetitions.js,写入以下内容:

import { useState, useEffect } from 'react';
import axios from 'axios';

const useCompetitions = () => {
  const [competitions, setCompetitions] = useState([]);
  const [defaultCompetitionValue, setDefaultCompetitionValue] = useState({});
  // 新增加载状态,方便组件判断渲染逻辑
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchCompetitions = async () => {
      try {
        const res = await axios.get('http://localhost:4000/app/getCompetitions');
        // 按不可变原则生成新的格式化数组,不直接修改原状态
        const formattedList = res.data.map(item => ({
          value: item.name,
          label: item.name,
          id: item._id
        }));
        setCompetitions(formattedList);
        if (formattedList.length > 0) {
          setDefaultCompetitionValue({
            value: formattedList[0].value,
            label: formattedList[0].value
          });
        }
      } catch (err) {
        console.error('获取赛事列表失败:', err);
      } finally {
        setLoading(false);
      }
    };

    fetchCompetitions();
  }, []); // 清空依赖,仅组件挂载时请求一次,避免无限触发

  return {
    competitions,
    defaultCompetitionValue,
    loading
  };
};

export default useCompetitions;

第二步:修改原App.js使用自定义Hook

替换原有逻辑如下:

import React from 'react';
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Competitions from './components/Competitions';
import CompetitionForm from './components/CompetitionForm';
import Navigation from './components/Navigation';
import useCompetitions from './hooks/useCompetitions';
import './styles/App.css'
 
const App = () => {
  const { competitions, defaultCompetitionValue, loading } = useCompetitions();

  if(loading || competitions.length === 0 || !defaultCompetitionValue) {
    return <></>
  }

  return (
    <div className = "container">
      <Router>
        <div className = "sidebar">
          <Navigation />
        </div>
        <div className = "content">
          <Switch>
            <Route exact path = "/homepage">
              <Competitions
                competitions = {competitions}
                defaultCompetitionValue = {defaultCompetitionValue}
              />
            </Route>
            <Route exact path = "/CompetitionForm">
              <CompetitionForm competitions = {competitions}/>
            </Route>
          </Switch>
        </div>
      </Router>
    </div>
  )
};

export default App;

第三步:其他组件复用逻辑

任何和App无关联的组件,直接导入自定义Hook调用即可拿到对应数据:

import useCompetitions from '../hooks/useCompetitions';

const 你的组件 = () => {
  const { competitions, defaultCompetitionValue, loading } = useCompetitions();
  // 后续写你自己的业务逻辑即可
}

如果需要避免多组件调用时重复发起请求,可以结合React Context全局存储数据,或者在自定义Hook内增加请求缓存逻辑即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 12:24:03