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

React中使用useEffect触发数据请求是否属于不良实践?

使用useEffect是否属于不良开发实践?是否需要因为重渲染问题尽可能避免使用useEffect?

该问题来自一次代码评审工作,我和同事对相关场景的实现方案存在意见分歧。
我们正在开发一款文档展示类应用,展示的文档支持按时间正序或倒序排列,排序规则由应用顶部栏的按钮控制,默认值为时间正序;该排序值存储在Redux全局状态中,所有拉取文档的接口请求都需要传入该参数。
当前示例的实现逻辑为:点击按钮更新sortOrder状态,以状态变化作为副作用触发数据拉取。按照我的理解,sortOrder状态变化时组件会触发一次渲染,数据拉取完成后组件会再次触发渲染。

近似伪代码

interface AppState = {
    sortOrder: SortOrder;
    documentation: Documentation[];
}

reducer(){
    case toggleSortOrder:
        const order = state.sortOrder === 'asc' ? 'desc' : 'asc';
        return {
            ...state,
            sortOrder: order;
        }
}

const AppBar = () => {
    const dispatch = useDispatch();
    return <div><button onClick={() => dispatch(toggleSortOrder)}>Change sort order</button> 
       </div>;
}

const DocumentationList = ({type}: {type: DocumentationType}) => {
    const dispatch = useDispatch();
    const sortOrder = useSelector((state) => state.appState.sortOrder);
    const documentation = useSelector((state) => state.appState.documentation);

    useEffect(() => {
        // action is caught by redux-saga and a call to docApi is made through axios
        dispatch(getDocumentation.request(type, sortOrder))
    },[sortOrder, type, dispatch]);

    return documentation.map((doc) => <Documentation key={doc.id} data={doc} />);
}

注:上述伪代码修正了原示例中部分语法疏漏(比如扩展运算符书写错误、onClick未包裹函数、组件props解构缺失、useEffect依赖不全、列表缺key等),不改变原有实现逻辑。

请问上述实现是否属于不良实践?是否应该避免使用useEffect,改为在按钮点击时直接触发数据拉取、在redux-saga中更新sortOrder状态?
我查阅官方文档和技术博客时,大多只看到useEffect的使用方式和适用场景示例,未找到该类场景下的明确最佳实践指引。


回答

核心结论

你当前的useEffect实现不属于不良实践,完全不需要为了规避两次渲染刻意去掉useEffect,两种实现方案没有绝对的对错,只有场景适配的区别,不存在必须二选一的铁则。

关于重渲染问题的说明

排序状态变更触发一次渲染、接口返回后数据更新触发第二次渲染,是React状态驱动模型下的正常流程,不属于需要优化的冗余渲染:

  • 第一次渲染可以同步给用户操作反馈:比如给排序按钮加激活态、列表区域展示loading占位,让用户明确感知到操作已生效
  • 第二次渲染是数据就绪后的正常内容更新,整个链路符合React「状态变更→调度副作用→副作用执行后更新状态→视图同步」的核心设计逻辑,没有多余的性能开销。

两种方案的优劣势对比

  • 当前useEffect响应状态变更拉取数据的方案
    优势是数据拉取逻辑和排序状态完全解耦:后续不管是顶部按钮修改排序、URL参数同步排序值、读取用户默认偏好设置、其他业务模块触发排序变更,只要全局的sortOrder状态发生变化,文档列表就会自动拉取对应排序规则的数据,不需要在所有触发排序变更的位置重复编写数据拉取逻辑,长期可维护性更强。
    这种写法只需要注意两个细节:一是补全useEffect的全部依赖项(比如示例中的type参数必须加入依赖数组,否则文档类型切换时会用旧的排序值拉取数据);二是做好请求竞态处理,比如快速连续切换排序时,只保留最后一次发起请求的返回结果,避免旧请求晚返回覆盖新数据。
  • 按钮点击直接触发拉取、saga同步更新排序状态的方案
    优势是用户操作到请求发起的链路更直观,少了一次effect调度的微小开销;但劣势非常明显:数据拉取逻辑和按钮操作强耦合,后续只要新增其他修改sortOrder的入口,就必须手动同步调用数据拉取的action,非常容易出现「排序值已经变了,但列表数据没刷新」的一致性bug。

useEffect的正确使用原则

从来不存在「要尽可能避免使用useEffect」的开发规则,官方明确不推荐的是滥用useEffect制造多余的更新链路:比如把可以在渲染阶段直接通过props/state计算得到的派生值,单独存成state再用useEffect做同步更新,这类会制造冗余渲染的写法才是需要规避的不良实践。
而数据请求、订阅外部事件、操作DOM这类和React渲染流程无关的外部副作用,用useEffect响应依赖状态的变化来触发,是完全符合React设计预期的标准用法。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 21:09:17