简体   繁体   English

React 组件正在无限地重新渲染

[英]React component is re-rendering infinitely

First, I am really new to react;首先,我真的很陌生; so, apologies, for beginner questions.所以,抱歉,对于初学者的问题。

I have a React app with Redux and Redux Saga.我有一个带有 Redux 和 Redux Saga 的 React 应用程序。

One of the components looks like this:其中一个组件如下所示:

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);

The sagas that process ProgramCategoryActions.fetchAll.start is as follows:处理ProgramCategoryActions.fetchAll.start如下:

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);
}

Everything make sense, but what happens my action code is executed over and over again.一切都说得通,但是发生的事情我的动作代码会一遍又一遍地执行。 Digging into it a bit more, it appears that the effect hook is also executed over and over again.再深入研究一下,似乎效果钩子也被一遍又一遍地执行。 If I understand it correctly, it happens because the data in state is changing, the component is getting re-rendered again.如果我理解正确,发生这种情况是因为状态中的数据正在发生变化,组件再次重​​新渲染。 But, it leads to infinite loop.但是,它导致无限循环。 What am I doing wrong?我究竟做错了什么? What is the correct way to setup this kind of component?设置这种组件的正确方法是什么?

One of the options that I found is to change the saga to:我发现的选项之一是将传奇更改为:

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));
  }
}

So, it only calls the api once;所以,它只调用 api 一次; and it seem to work fine this way.它似乎以这种方式工作得很好。 But, is it the correct solution?但是,这是正确的解决方案吗?

As suggested in the comments, I tried adding dependency to the effect:正如评论中所建议的,我尝试为效果添加依赖项:

useEffect(() => {
    fetchAllCategories();
  }, []);

Now, I am getting an error:现在,我收到一个错误:

./src/program/ProgramCategoriesTable/ProgramCategoriesTable.tsx Line 37:6: React Hook useEffect has a missing dependency: 'fetchAllCategories'. ./src/program/ProgramCategoriesTable/ProgramCategoriesTable.tsx 第 37:6 行:React Hook useEffect 缺少依赖项:'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如果 'fetchAllCategories' 更改过于频繁,请找到定义它的父组件并将该定义包装在 useCallback react-hooks/exhaustive-deps 中

The issue is here,问题在这里,

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

From react docs: Does useEffect run after every render?来自 react 文档:useEffect 是否在每次渲染后运行? Yes!是的! By default, it runs both after the first render and after every update.默认情况下,它会在第一次渲染之后和每次更新之后运行。 (We will later talk about how to customize this.) Instead of thinking in terms of “mounting” and “updating”, you might find it easier to think that effects happen “after render”. (我们稍后将讨论如何自定义它。)与其考虑“安装”和“更新”,您可能会发现更容易认为效果发生在“渲染之后”。 React guarantees the DOM has been updated by the time it runs the effects. React 保证 DOM 在它运行效果时已经更新。

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

You have to pass and array of dependecies at the end.你必须在最后传递和依赖数组。

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

Hope this helps!希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM