为什么在下面的代码中会出现这种无限循环?反应和使用状态

Why is this infinite loop happening in the following code? React & useState

我将为您提供我编写的组件的两个版本。为什么第一个给我一个无限循环,而第二个工作正常?

我隔离了问题,但我想知道为什么逻辑不符合 'under the hood'。毫无疑问,来自 useState

的一些黑魔法
//THROWS INFINTE LOOP ERROR
import React, { useState } from 'react';
import PropTypes from 'prop-types';

import { Popover, PopoverHeader, PopoverBody } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faInfoCircle } from '@fortawesome/pro-light-svg-icons';

const HelpIcon = (props) => {
  HelpIcon.propTypes = {
    title: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
    id: PropTypes.string.isRequired,
    children: PropTypes.node.isRequired
  };

  const [isOpen, toggleIsOpen] = useState(false);

  return (
    <React.Fragment>
      <span
        className="pointer text-body"
        id={props.id}
      >
        <FontAwesomeIcon icon={faInfoCircle} />
      </span>
      <Popover
        trigger="legacy"
        placement="left"
        isOpen={isOpen}
        target={props.id}
        toggle={toggleIsOpen(!isOpen)}{//<-----look here!!!!!!!!!!!!!!!}
      >
        {props.title !== false && (
          <PopoverHeader className="text-body bg-light">
            {props.title}
          </PopoverHeader>
        )}

        <PopoverBody className="text-xs cart__rebate_description text-body bg-white">
          {props.children}
        </PopoverBody>
      </Popover>
    </React.Fragment>
  );
};

export default HelpIcon;

...和...

//THIS ONE WORKS
//NOTICE THE EXTRA FUNCTION THAT CALLS USESTATE, INSTEAD OF CALLING IT DIRECTLY
import React, { useState } from 'react';
import PropTypes from 'prop-types';

import { Popover, PopoverHeader, PopoverBody } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faInfoCircle } from '@fortawesome/pro-light-svg-icons';

const HelpIcon = (props) => {
  HelpIcon.propTypes = {
    title: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
    id: PropTypes.string.isRequired,
    children: PropTypes.node.isRequired
  };

  const [isOpen, toggleIsOpen] = useState(false);

  const toggle = () => toggleIsOpen(!isOpen);

  return (
    <React.Fragment>
      <span
        className="pointer text-body"
        id={props.id}
      >
        <FontAwesomeIcon icon={faInfoCircle} />
      </span>
      <Popover
        trigger="legacy"
        placement="left"
        isOpen={isOpen}
        target={props.id}
        toggle={toggle}
      >
        {props.title !== false && (
          <PopoverHeader className="text-body bg-light">
            {props.title}
          </PopoverHeader>
        )}

        <PopoverBody className="text-xs cart__rebate_description text-body bg-white">
          {props.children}
        </PopoverBody>
      </Popover>
    </React.Fragment>
  );
};

export default HelpIcon;

您在第一个示例中立即调用 toggleIsOpen。如果你想用 toggle prop 提供的参数以外的参数调用它,你应该总是用另一个函数包装它。

这是错误的,它会导致循环:

toggle={toggleIsOpen(!isOpen)} // you call the function in a loop

应该是:

toggle={() => toggleIsOpen(!isOpen)}

如果你真的想按自己的方式使用它,你必须像这样使用双箭头功能:

const toggle = isOpen => () => {
    toggleIsOpen(isOpen)
}

// and use it like

toggle={toggle(!isOpen)}

它导致无限,因为改变状态会导致重新渲染反应;如果您的函数调用不会导致重新渲染,它将起作用(有点,不会导致无限循环)像这样

foo = () => console.log(9)

toggle={foo()}

但由于设置状态会导致重新呈现您的应用卡住;这是调用堆栈(有点)

  1. render被调用;

  2. 它将达到set-state

  3. state 变化

  4. 再次呼叫 render

  5. 它将达到set-state

  6. state 变化

  7. 再次呼叫 render

和....