React.js useState hook 导致过多的重新渲染并且无法更新我的状态

Posted

技术标签:

【中文标题】React.js useState hook 导致过多的重新渲染并且无法更新我的状态【英文标题】:React.js useState hook causes too many re-renders and cant update my state 【发布时间】:2020-08-09 17:53:12 【问题描述】:

我是一名试图掌握 React 窍门的学生。到目前为止,我希望能够将从 API 接收的数据数组切割成块,以便以后可以使用分页效果。我无法弄清楚为什么函数 arrayChunk() 最终会导致过多的渲染。如何设置我的函数以将 chunkedArray 推入 setChunkedYearArray? 这是我的完整代码:




import React,  useState, useEffect  from 'react';
import InformationBox from '../../component/InformationBox/InformationBox';
import arrowButton from '../../assets/arrow-button.svg';

const InformationBoxLayout = (props) => 
    const [activeYear, setActiveYear] = useState([]);
    const [chunkedYearArray, setChunkedYearArray] = useState([]);   
useEffect(() => 
        axios
            .get('http://www.mocky.io/v2/5ea446a43000005900ce2ca3')
            .then((response) =>
                setActiveYear(
                    response.data.timelineInfo.filter((item) => item.year === '2018')
                )
            );
    , []);

    const arrayChunk = (array, chunkSize) => 
        const chunkedArray = [];
        let clonedArray = [...array];
        const splitPieces = Math.ceil(clonedArray.length / chunkSize);
        for (let i = 0; i < splitPieces; i++) 
            chunkedArray.push(clonedArray.splice(0, chunkSize));
        
        setChunkedYearArray(chunkedArray);
    ;

    return (
        <div className=style.infoBoxLayoutStyle>
            <button className=style.leftArrow>
                <img src=arrowButton alt='previous-page-button' />
            </button>
            activeYear.length > 6
                ? arrayChunk(activeYear, 6)
                : console.log('no split')

            <button className=style.rightArrow>
                <img
                    src=arrowButton
                    alt='next-page-button'
                    className=style.rotateArrowRight
                />
            </button>
        </div>
    );
;

export default InformationBoxLayout;

【问题讨论】:

【参考方案1】:

任何时候你调用 setState 组件都会重新渲染,所以你不能在渲染函数中调用 setState。没有理由为chunkedYearArray 使用状态。只需从函数返回值,然后使用它:

const InformationBoxLayout = props => 
  const [activeYear, setActiveYear] = useState([]);

  useEffect(() => 
    axios
      .get("http://www.mocky.io/v2/5ea446a43000005900ce2ca3")
      .then(response =>
        setActiveYear(
          response.data.timelineInfo.filter(item => item.year === "2018")
        )
      );
  , []);

  const arrayChunk = (array, chunkSize) => 
    const chunkedArray = [];
    let clonedArray = [...array];
    const splitPieces = Math.ceil(clonedArray.length / chunkSize);
    for (let i = 0; i < splitPieces; i++) 
      chunkedArray.push(clonedArray.splice(0, chunkSize));
    
    return chunkedArray;
  ;

  // Now you have calculated it, so use it for whatever you want in your JSX
  const chunkedYearArray = activeYear.length > 6
        ? arrayChunk(activeYear, 6)
        : [];

  return (
    <div className=style.infoBoxLayoutStyle>
      <button className=style.leftArrow>
        <img src=arrowButton  />
      </button>


      <button className=style.rightArrow>
        <img
          src=arrowButton
          
          className=style.rotateArrowRight
        />
      </button>
    </div>
  );
;

export default InformationBoxLayout;

或者,如果您真的希望它处于状态,您可以通过 API 调用在 then 中进行计算:

  const [chunkedYearArray, setChunkedYearArray] = useState([]);
  useEffect(() => 
    axios
      .get("http://www.mocky.io/v2/5ea446a43000005900ce2ca3")
      .then(response =>
        setActiveYear(
          response.data.timelineInfo.filter(item => item.year === "2018")
        )

        const arrayChunk = (array, chunkSize) => 
          const chunkedArray = [];
          let clonedArray = [...array];
          const splitPieces = Math.ceil(clonedArray.length / chunkSize);
          for (let i = 0; i < splitPieces; i++) 
            chunkedArray.push(clonedArray.splice(0, chunkSize));
          
          return chunkedArray;
        ;

        const newChunkedYearArray = activeYear.length > 6 ? arrayChunk(activeYear, 6)
        : [];

        // Because we are in useEffect we are only calculated once!
        setChunkedYearArray(newChunkedYearArray)
      );
  , []);

这里肯定有清理的余地,但我认为其中任何一个都会让你走上正轨。希望对您有所帮助!

【讨论】:

【参考方案2】:

您最终会调用arrayChunk 每个渲染设置一些状态并触发重新渲染。仅当 activeYear 更新应该有帮助时,才使用效果挂钩重新计算分块数组。

useEffect(() => 
  activeYear.length > 6 && arrayChunk(activeYear, 6);
, [activeYear]);

然后只渲染 chunkedYearArray

【讨论】:

以上是关于React.js useState hook 导致过多的重新渲染并且无法更新我的状态的主要内容,如果未能解决你的问题,请参考以下文章

React Hook的注意事项--useState

无法在类组件 React.js 中使用 useState

为啥我没有取回 useState 的新值 - React.JS?

在 React.js 中更新 useState 数组的特定变量(按其位置)?

React hooks的useState原理

React JS Typescript usestate 与布尔值