React 组件正在无限重新渲染
React component is re-rendering infinitely
首先,我真的是react新手;所以,很抱歉,对于初学者的问题。
我有一个带有 Redux 和 Redux Saga 的 React 应用程序。
其中一个组件如下所示:
import { TableContainer, TableHead, TableRow } from '@material-ui/core';
import Paper from '@material-ui/core/Paper';
import makeStyles from '@material-ui/core/styles/makeStyles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { ProgramCategory } from '../model/program-category';
import { ProgramCategoryItemRow } from '../ProgramGategoryItemRow/ProgramCategoryItemRow';
import { ProgramCategoryActions } from '../store/program-category.actions';
import { ProgramCategorySelectors } from '../store/program-category.selectors';
const useStyles = makeStyles({
table: {
width: '100%',
},
tableHeadCell: {
fontWeight: 'bold',
},
});
export interface ProgramCategoriesTableProps {
isLoaded: boolean;
categories: ProgramCategory[];
fetchAllCategories: () => void;
}
export const PureProgramCategoriesTable: React.FC<ProgramCategoriesTableProps> = ({
isLoaded,
categories,
fetchAllCategories,
}) => {
useEffect(() => {
console.error('in useEffect');
fetchAllCategories();
});
const styles = useStyles();
return (
<TableContainer component={Paper}>
// the rest
<TableBody>
{categories.map(c => (
<ProgramCategoryItemRow category={c} />
))}
</TableBody>
</TableContainer>
);
};
const mapStateToProps = createSelector(
[ProgramCategorySelectors.isLoaded, ProgramCategorySelectors.getAll],
(isLoaded, categories) => ({ isLoaded, categories }),
);
const mapDispatchToProps = {
fetchAllCategories: ProgramCategoryActions.fetchAll.start,
};
export const ProgramCategoriesTable = connect(mapStateToProps, mapDispatchToProps)(PureProgramCategoriesTable);
处理ProgramCategoryActions.fetchAll.start
的sagas如下:
import { call, put, takeLatest } from 'redux-saga/effects';
import { ProgramCategoryApi } from '../services/program-category.api';
import { ProgramCategoryActions } from './program-category.actions';
function* handleFetchAll() {
try {
const categories = yield call(ProgramCategoryApi.fetchAll);
yield put(ProgramCategoryActions.fetchAll.success(categories));
} catch (e) {
yield put(ProgramCategoryActions.fetchAll.failure(e));
}
}
export function* programCategorySagas() {
yield takeLatest(ProgramCategoryActions.fetchAll.start.type, handleFetchAll);
}
一切都说得通,但我的动作代码却一遍又一遍地执行。再深入一点,效果钩子似乎也被一遍又一遍地执行。
如果我理解正确的话,它的发生是因为状态中的数据正在改变,组件再次被重新渲染。但是,它会导致无限循环。
我究竟做错了什么?设置这种组件的正确方法是什么?
我找到的一个选项是将 saga 更改为:
function* handleFetchAll() {
try {
const alreadyLoaded = select(ProgramCategorySelectors.isLoaded);
if (!alreadyLoaded) {
const categories = yield call(ProgramCategoryApi.fetchAll);
yield put(ProgramCategoryActions.fetchAll.success(categories));
}
} catch (e) {
yield put(ProgramCategoryActions.fetchAll.failure(e));
}
}
所以,它只调用一次 api;这样似乎效果很好。但是,这是正确的解决方案吗?
按照评论中的建议,我尝试添加对效果的依赖:
useEffect(() => {
fetchAllCategories();
}, []);
现在,我收到一个错误:
./src/program/ProgramCategoriesTable/ProgramCategoriesTable.tsx Line
37:6: React Hook useEffect has a missing dependency:
'fetchAllCategories'. Either include it or remove the dependency
array. If 'fetchAllCategories' changes too often, find the parent
component that defines it and wrap that definition in useCallback
react-hooks/exhaustive-deps
问题就在这里,
useEffect(() => {
console.error('in useEffect');
fetchAllCategories();
});
来自 React 文档:每次渲染后是否使用 Effect 运行?是的!默认情况下,它 运行 在第一次渲染之后和每次更新之后。 (稍后我们将讨论如何自定义它。)与其从“安装”和“更新”的角度思考,您可能会更容易认为效果发生在“渲染之后”。 React 保证 DOM 在 运行 产生效果时已经更新。
https://reactjs.org/docs/hooks-effect.html
你必须在最后传递依赖项数组。
useEffect(() => {
console.error('in useEffect');
fetchAllCategories();
}, []);
希望对您有所帮助!
首先,我真的是react新手;所以,很抱歉,对于初学者的问题。
我有一个带有 Redux 和 Redux Saga 的 React 应用程序。
其中一个组件如下所示:
import { TableContainer, TableHead, TableRow } from '@material-ui/core';
import Paper from '@material-ui/core/Paper';
import makeStyles from '@material-ui/core/styles/makeStyles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { ProgramCategory } from '../model/program-category';
import { ProgramCategoryItemRow } from '../ProgramGategoryItemRow/ProgramCategoryItemRow';
import { ProgramCategoryActions } from '../store/program-category.actions';
import { ProgramCategorySelectors } from '../store/program-category.selectors';
const useStyles = makeStyles({
table: {
width: '100%',
},
tableHeadCell: {
fontWeight: 'bold',
},
});
export interface ProgramCategoriesTableProps {
isLoaded: boolean;
categories: ProgramCategory[];
fetchAllCategories: () => void;
}
export const PureProgramCategoriesTable: React.FC<ProgramCategoriesTableProps> = ({
isLoaded,
categories,
fetchAllCategories,
}) => {
useEffect(() => {
console.error('in useEffect');
fetchAllCategories();
});
const styles = useStyles();
return (
<TableContainer component={Paper}>
// the rest
<TableBody>
{categories.map(c => (
<ProgramCategoryItemRow category={c} />
))}
</TableBody>
</TableContainer>
);
};
const mapStateToProps = createSelector(
[ProgramCategorySelectors.isLoaded, ProgramCategorySelectors.getAll],
(isLoaded, categories) => ({ isLoaded, categories }),
);
const mapDispatchToProps = {
fetchAllCategories: ProgramCategoryActions.fetchAll.start,
};
export const ProgramCategoriesTable = connect(mapStateToProps, mapDispatchToProps)(PureProgramCategoriesTable);
处理ProgramCategoryActions.fetchAll.start
的sagas如下:
import { call, put, takeLatest } from 'redux-saga/effects';
import { ProgramCategoryApi } from '../services/program-category.api';
import { ProgramCategoryActions } from './program-category.actions';
function* handleFetchAll() {
try {
const categories = yield call(ProgramCategoryApi.fetchAll);
yield put(ProgramCategoryActions.fetchAll.success(categories));
} catch (e) {
yield put(ProgramCategoryActions.fetchAll.failure(e));
}
}
export function* programCategorySagas() {
yield takeLatest(ProgramCategoryActions.fetchAll.start.type, handleFetchAll);
}
一切都说得通,但我的动作代码却一遍又一遍地执行。再深入一点,效果钩子似乎也被一遍又一遍地执行。 如果我理解正确的话,它的发生是因为状态中的数据正在改变,组件再次被重新渲染。但是,它会导致无限循环。 我究竟做错了什么?设置这种组件的正确方法是什么?
我找到的一个选项是将 saga 更改为:
function* handleFetchAll() {
try {
const alreadyLoaded = select(ProgramCategorySelectors.isLoaded);
if (!alreadyLoaded) {
const categories = yield call(ProgramCategoryApi.fetchAll);
yield put(ProgramCategoryActions.fetchAll.success(categories));
}
} catch (e) {
yield put(ProgramCategoryActions.fetchAll.failure(e));
}
}
所以,它只调用一次 api;这样似乎效果很好。但是,这是正确的解决方案吗?
按照评论中的建议,我尝试添加对效果的依赖:
useEffect(() => {
fetchAllCategories();
}, []);
现在,我收到一个错误:
./src/program/ProgramCategoriesTable/ProgramCategoriesTable.tsx Line 37:6: React Hook useEffect has a missing dependency: 'fetchAllCategories'. Either include it or remove the dependency array. If 'fetchAllCategories' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
问题就在这里,
useEffect(() => {
console.error('in useEffect');
fetchAllCategories();
});
来自 React 文档:每次渲染后是否使用 Effect 运行?是的!默认情况下,它 运行 在第一次渲染之后和每次更新之后。 (稍后我们将讨论如何自定义它。)与其从“安装”和“更新”的角度思考,您可能会更容易认为效果发生在“渲染之后”。 React 保证 DOM 在 运行 产生效果时已经更新。
https://reactjs.org/docs/hooks-effect.html
你必须在最后传递依赖项数组。
useEffect(() => {
console.error('in useEffect');
fetchAllCategories();
}, []);
希望对您有所帮助!