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

