React组件无限地重新渲染

时间:2020-01-14 21:52:58

标签: javascript reactjs redux redux-saga

首先,我真的很陌生。抱歉,对于初学者的问题。

我有一个带有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);
}

一切都有意义,但是我的动作代码会一遍又一遍地执行。深入研究它,似乎效果钩也一遍又一遍地执行。 如果我正确理解它,则发生这种情况是因为状态中的数据正在更改,该组件将再次重新呈现。但是,这会导致无限循环。 我究竟做错了什么?设置这种组件的正确方法是什么?

我发现的一种选择是将传奇更改为:

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行 37:6:React Hook useEffect缺少依赖项: 'fetchAllCategories'。包括它或删除依赖项 数组。如果“ fetchAllCategories”更改过于频繁,请找到父项 定义它并将该定义包装在useCallback中的组件 反应钩/详尽的下降

1 个答案:

答案 0 :(得分:0)

问题在这里

useEffect(() => {
  console.error('in useEffect');
  fetchAllCategories();
});

来自react docs:useEffect是否在每次渲染后运行?是!默认情况下,它在第一次渲染后和每次更新后都运行。 (我们将在后面讨论如何自定义此功能。)您可能会发现更容易想到效果发生在“渲染后”,而不是“安装”和“更新”。 React保证DOM在运行效果时已被更新。

https://reactjs.org/docs/hooks-effect.html

您必须在最后传递一系列依赖项。

useEffect(() => {
  console.error('in useEffect');
  fetchAllCategories();
}, []);

希望这会有所帮助!