为什么在下面的代码中会出现这种无限循环?反应和使用状态
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()}
但由于设置状态会导致重新呈现您的应用卡住;这是调用堆栈(有点)
render
被调用;
它将达到set-state
state
变化
再次呼叫 render
它将达到set-state
state
变化
再次呼叫 render
和....
我将为您提供我编写的组件的两个版本。为什么第一个给我一个无限循环,而第二个工作正常?
我隔离了问题,但我想知道为什么逻辑不符合 '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()}
但由于设置状态会导致重新呈现您的应用卡住;这是调用堆栈(有点)
render
被调用;它将达到
set-state
state
变化再次呼叫
render
它将达到
set-state
state
变化再次呼叫
render
和....