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

React组件使用Axios请求时3次渲染是否正常及原因

React组件3次渲染是否正常?原因解析

代码实现

useBaseAxios基础请求钩子

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

const baseAxios = axios.create({
    baseURL: "http://127.0.0.1:8000/api/v1",
});

const useBaseAxios = ({ method = "GET", url }) => {
    const [data, setData] = useState(null);
    const [isLoading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const controller = new AbortController();
        const signal = controller.signal;

        const fetchData = async () => {
            try {
                const response = await baseAxios.request({ method, url, signal })
                setData(response.data)

            } catch (error) {
                if (!axios.isCancel(error)){
                    setError(error)
                }
            } finally {
                setLoading(false)
            }
        }

        if (!data) {
            fetchData()
        }

        return () => {
            controller.abort()
        }

    }, [method, url])

    return [data, isLoading, error]
};

export default useBaseAxios;

useClosestLessons业务钩子

import useBaseAxios from "./useBaseAxios";

function useClosestLessons() {
    const [data, isLoading, error] = useBaseAxios({ method: 'GET', url: '/lessons' })

    let lessonsSchedule = [];
    let currentLesson = {};

    if (data) {
        lessonsSchedule = [...data]
        currentLesson = lessonsSchedule[0]
    }

    return [lessonsSchedule, currentLesson, isLoading, error]
}

export default useClosestLessons

Main组件

import React from "react"; 
import './Main.scss';
import useClosestLessons from "../../hooks/fetch-hooks/useClosestLessons";
import { useState, useEffect, useCallback } from "react";
import axios from "axios";

const Main = () => {
    const [lessonsSchedule, currentLessons, isLoading, error] = useClosestLessons();

    console.log(lessonsSchedule)
    console.log(currentLessons)

    if (isLoading) return (<p>Загрузка...</p>)

    return(...)

问题描述

使用上述代码开发React应用,未开启StrictMode,原本预期控制台先输出null、null,随后输出目标数组与对象,但实际显示Main组件发生了3次渲染。请问该现象是否正常?若正常,原因是什么?

回答

这个现象是正常的,三次渲染的触发原因如下:

  1. 初始挂载渲染:组件首次加载时,useBaseAxios的data为初始值null,isLoading为true。此时useClosestLessons会返回空数组[]和空对象{},控制台打印这两个值。
  2. data状态更新触发的渲染:接口请求成功后,执行setData(response.data)修改data状态,触发组件第二次渲染。这次渲染时,lessonsSchedule和currentLesson会基于接口返回的数据生成对应数组和对象。
  3. isLoading状态更新触发的渲染:请求完成后进入finally块,执行setLoading(false)修改加载状态,触发组件第三次渲染。

额外说明两点:

  • 你预期的null输出不会出现,因为useClosestLessons中当data为null时,已经给lessonsSchedule和currentLesson分别赋值了空数组和空对象。
  • 之所以会出现两次状态更新触发的渲染,是因为在React 18及之前版本中,异步回调(比如你的fetchData异步函数)里的状态更新不会被批量处理,setData和setLoading这两个状态更新会分别触发一次渲染,加上初始渲染就形成了三次渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 02:24:58