244

我正在尝试从我的渲染视图重构以下代码:

<Button href="#" active={!this.state.singleJourney} onClick={this.handleButtonChange.bind(this,false)} >Retour</Button>

到绑定在构造函数中的版本。原因是渲染视图中的绑定会给我带来性能问题,尤其是在低端手机上。

我创建了以下代码,但我不断收到以下错误(很多)。看起来应用程序进入了循环:

Warning: setState(...): Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.

下面是我使用的代码:

var React = require('react');
var ButtonGroup = require('react-bootstrap/lib/ButtonGroup');
var Button = require('react-bootstrap/lib/Button');
var Form = require('react-bootstrap/lib/Form');
var FormGroup = require('react-bootstrap/lib/FormGroup');
var Well = require('react-bootstrap/lib/Well');

export default class Search extends React.Component {

    constructor() {
        super();

        this.state = {
            singleJourney: false
        };

        this.handleButtonChange = this.handleButtonChange.bind(this);
    }

    handleButtonChange(value) {
        this.setState({
            singleJourney: value
        });
    }

    render() {

        return (
            <Form>

                <Well style={wellStyle}>

                    <FormGroup className="text-center">

                        <ButtonGroup>
                            <Button href="#" active={!this.state.singleJourney} onClick={this.handleButtonChange(false)} >Retour</Button>
                            <Button href="#" active={this.state.singleJourney} onClick={this.handleButtonChange(true)} >Single Journey</Button>
                        </ButtonGroup>
                    </FormGroup>

                </Well>

            </Form>
        );
    }
}

module.exports = Search;
4

11 回答 11

368

看起来您不小心handleButtonChange在渲染方法中调用了该方法,您可能想要这样做onClick={() => this.handleButtonChange(false)}

如果您不想在 onClick 处理程序中创建 lambda,我认为您需要有两个绑定方法,每个参数一个。

constructor

this.handleButtonChangeRetour = this.handleButtonChange.bind(this, true);
this.handleButtonChangeSingle = this.handleButtonChange.bind(this, false);

render方法中:

<Button href="#" active={!this.state.singleJourney} onClick={this.handleButtonChangeSingle} >Retour</Button>
<Button href="#" active={this.state.singleJourney} onClick={this.handleButtonChangeRetour}>Single Journey</Button>
于 2016-05-23T09:52:57.103 回答
17

为了更好地理解,我给出了一个通用示例,在下面的代码中

render(){
    return(
      <div>

        <h3>Simple Counter</h3>
        <Counter
          value={this.props.counter}
          onIncrement={this.props.increment()} <------ calling the function
          onDecrement={this.props.decrement()} <-----------
          onIncrementAsync={this.props.incrementAsync()} />
      </div>
    )
  }

提供道具时,我直接调用该函数,这将执行无限循环并会给您该错误,删除该函数调用一切正常。

render(){
    return(
      <div>

        <h3>Simple Counter</h3>
        <Counter
          value={this.props.counter}
          onIncrement={this.props.increment} <------ function call removed
          onDecrement={this.props.decrement} <-----------
          onIncrementAsync={this.props.incrementAsync} />
      </div>
    )
  }
于 2018-10-24T09:25:13.453 回答
13

这通常发生在你打电话时

onClick={this.handleButton() }- 注意()而不是:

onClick={this.handleButton} - 注意这里我们在初始化的时候并没有调用这个函数

于 2019-10-11T22:35:25.773 回答
6

问题在这里:onClick={this.handleButtonChange(false)}

当您传递this.handleButtonChange(false)给 onClick 时,您实际上是在调用函数value = false并将 onClick 设置为函数的返回值,该返回值未定义。此外,调用this.handleButtonChange(false)then 调用 this.setState()会触发重新渲染,从而导致无限渲染循环。

解决方案是在 lambda: 中传递函数onClick={() => this.handleButtonChange(false)}。在这里,您将 onClick 设置为等于单击按钮时将调用 handleButtonChange(false) 的函数。

以下示例可能会有所帮助:

function handleButtonChange(value){
  console.log("State updated!")
}

console.log(handleButtonChange(false))
//output: State updated!
//output: undefined

console.log(() => handleButtonChange(false))
//output: ()=>{handleButtonChange(false);}
于 2020-06-18T18:14:33.050 回答
4

如果您尝试将参数添加到 中的处理程序recompose,请确保您在处理程序中正确定义了参数。它本质上是一个柯里化函数,因此您要确保需要正确数量的参数。这个页面有一个很好的例子,说明使用处理程序的参数。

示例(来自链接):

withHandlers({
  handleClick: props => (value1, value2) => event => {
    console.log(event)
    alert(value1 + ' was clicked!')
    props.doSomething(value2)
  },
})

为您的孩子 HOC 和父母

class MyComponent extends Component {
  static propTypes = {
    handleClick: PropTypes.func, 
  }
  render () {
    const {handleClick} = this.props
    return (
      <div onClick={handleClick(value1, value2)} />
    )
  }
}

这避免了从处理程序中编写匿名函数来修补解决在处理程序上未提供足够参数名称的问题。

于 2017-06-06T16:09:11.923 回答
2

从反应文档将参数传递给事件处理程序

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
于 2018-01-03T05:21:29.497 回答
2

render()在调用中完成的任何状态更改都会发出同样的警告。

一个很难找到的例子:当基于状态数据渲染一个多选的 GUI 组件时,如果状态没有要显示的东西,调用resetOptions()被认为是该组件的状态更改。

显而易见的解决方法是使用resetOptions()incomponentDidUpdate()而不是render().

于 2018-11-27T12:08:34.820 回答
1

问题当然是在使用 onClick 处理程序渲染按钮时的 this 绑定。解决方案是在渲染时调用动作处理程序时使用箭头函数。像这样: onClick={ () => this.handleButtonChange(false) }

于 2018-09-04T08:15:45.323 回答
1

I got the same error when I was calling

this.handleClick = this.handleClick.bind(this);

in my constructor when handleClick didn't exist

(I had erased it and had accidentally left the "this" binding statement in my constructor).

Solution = remove the "this" binding statement.

于 2017-09-06T10:19:34.173 回答
0

我用来为组件打开 Popover 的解决方案是reactstrap (React Bootstrap 4 components)

    class Settings extends Component {
        constructor(props) {
            super(props);

            this.state = {
              popoversOpen: [] // array open popovers
            }
        }

        // toggle my popovers
        togglePopoverHelp = (selected) => (e) => {
            const index = this.state.popoversOpen.indexOf(selected);
            if (index < 0) {
              this.state.popoversOpen.push(selected);
            } else {
              this.state.popoversOpen.splice(index, 1);
            }
            this.setState({ popoversOpen: [...this.state.popoversOpen] });
        }

        render() {
            <div id="settings">
                <button id="PopoverTimer" onClick={this.togglePopoverHelp(1)} className="btn btn-outline-danger" type="button">?</button>
                <Popover placement="left" isOpen={this.state.popoversOpen.includes(1)} target="PopoverTimer" toggle={this.togglePopoverHelp(1)}>
                  <PopoverHeader>Header popover</PopoverHeader>
                  <PopoverBody>Description popover</PopoverBody>
                </Popover>

                <button id="popoverRefresh" onClick={this.togglePopoverHelp(2)} className="btn btn-outline-danger" type="button">?</button>
                <Popover placement="left" isOpen={this.state.popoversOpen.includes(2)} target="popoverRefresh" toggle={this.togglePopoverHelp(2)}>
                  <PopoverHeader>Header popover 2</PopoverHeader>
                  <PopoverBody>Description popover2</PopoverBody>
                </Popover>
            </div>
        }
    }
于 2018-06-14T16:36:28.730 回答
0

onClick 函数必须通过一个返回 handleButtonChange() 方法的函数。否则它将自动运行,并以错误/警告结束。使用以下方法解决问题。

onClick={() => this.handleButtonChange(false)}

于 2021-09-29T16:44:59.307 回答