axios GET请求触发RangeError maximum call stack size exceeded报错排查
问题原因
栈溢出的核心原因是组件无限重渲染循环:
你当前把columns、actions、options这些传给Material Table的配置对象直接写在了组件函数体内,React组件每次重渲染时,这些对象都会生成全新的引用。
触发循环的流程如下:
- 初始化调用
fetchRequests,请求成功后执行setRequests更新state,触发组件重渲染 - 重渲染生成新的
columns/actions/options对象,Material Table检测到传入的props发生变化,触发自身重渲染 - 部分版本的Material Table在data和配置props同时变化时,会二次触发重渲染逻辑,又反过来触发父组件重渲染,形成无限循环,最终导致栈大小超限
点击刷新按钮时不会触发问题的原因是:刷新时组件已经完成首次挂载,配置引用变化和data更新的时序和初始化阶段不同,没有触发循环逻辑。
解决方案
把所有传给Material Table的配置项的引用固定,避免不必要的重渲染:
- 将不依赖组件内部state/props的静态配置直接移到组件外部
- 依赖内部变量的配置用
useMemo包裹,固定引用 - 请求方法用
useCallback包裹,避免引用变化
修改后的核心代码示例
import React, { useState, useEffect, useMemo, useCallback } from 'react'; import { withStyles } from '@material-ui/core/styles'; import { FormattedMessage, injectIntl } from 'react-intl'; import { Typography, Paper } from '@material-ui/core'; import MaterialTable from 'material-table'; import axios from 'axios'; import { appConfig } from '../../../config'; import drawerStyle from '../../../styles/drawers'; // 静态配置移到组件外部 const styles = (theme) => ({ drawerPaper: drawerStyle(theme).drawerSmall, }); const components = { Container: ({ children }) => { return <Paper elevation={0}>{children}</Paper>; } }; // 静态列配置移到外部 const columns = [ { title: <FormattedMessage id="administration.demoRequests.table.id" />, field: 'id', }, { title: <FormattedMessage id="administration.demoRequests.table.company" />, field: 'company', }, { title: <FormattedMessage id="administration.demoRequests.table.email" />, field: 'email', } ]; const url = `${appConfig.URL_REST}administration/demo_registration_request`; const DemoRequests = (props) => { const { intl } = props; const [requests, setRequests] = useState([]); // 固定config引用 const config = useMemo(() => ({ headers: { Authorization: `Bearer ${localStorage.getItem('token')}` }, }), []); // 固定请求方法引用 const fetchRequests = useCallback(() => { axios.get(url, config) .then((res) => { setRequests(res.data.demoRequests); console.log(res); }) .catch(error => console.log(`Error: ${error}`)); }, [config]); // 固定table options引用 const tableOptions = useMemo(() => ({ padding: 'dense', searchFieldStyle: { margin: '40px' }, rowStyle: { fontSize: '0.8125rem', padding: '0px' }, cellStyle: { padding: '2px 16px 1px 16px', maxHeight: '36px', }, }), []); // 固定actions引用,依赖intl和fetchRequests const tableActions = useMemo(() => [ { icon: 'refresh', tooltip: intl.formatMessage({ id: 'administration.demoRequests.table.tooltip.refresh.data' }), isFreeAction: true, onClick: () => fetchRequests(), } ], [intl, fetchRequests]); useEffect(() => { fetchRequests(); }, [fetchRequests]); return ( <> <MaterialTable title={ <Typography variant="h5" component="span"> <FormattedMessage id="administration.demoRequests.table.title" /> </Typography> } columns={columns} data={requests} options={tableOptions} actions={tableActions} /> </> ); } export default injectIntl( withStyles(styles, { withTheme: true })(DemoRequests) );
内容的提问来源于stack exchange,提问作者Ati
相关产品推荐
相关产品推荐

