从依赖于要渲染的状态的元素调用setState()时不会更改状态

时间:2018-07-10 18:22:38

标签: javascript reactjs state

我有一个类似这样的小组件(下面的代码简化为所需的部分),在更新状态时表现得很奇怪。

class Componenent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {showStuff: false};
  }

  render() {
    return(
      //Markup
        {this.state.showStuff && (
          <button onClick={() => this.setState({showStuff: false})} />
        )}
      // More Markup
    );
  }
}

状态会在组件中的其他位置更新,因此单击按钮时,属性为true。 单击也会触发setState函数(将执行回调),但是状态不会更新。


我的猜测是它不会更新,因为该函数是由直接依赖于状态prop可见的元素调用的。
我发现,在状态中添加另一个道具test: true并在单击按钮时将该属性更改为false也会触发showStuff道具变为false。因此,当我进行奇怪的修改时,它就可以工作。

有人可以向我解释这种奇怪的行为吗?我无法喘息为什么上面的代码片段无法按预期工作。


这是整个组件:

class ElementAdd extends React.Component {
    constructor(props) {
        super(props);

        this.defaultState = {
            showElementWheel: false,
            test: true
        };

        this.state = this.defaultState;
    }

    handleAddCardClick() {
        if (this.props.onCardAdd) {
            this.props.onCardAdd({
                type: ElementTypes.card,
                position: this.props.index
            });
        }
    }

    handleAddKnowledgeClick() {
        if (this.props.onCardAdd) {
            this.props.onCardAdd({
                type: ElementTypes.knowledge,
                position: this.props.index
            });
        }
    }

    handleTabPress(e) {
        if (e.key === 'Tab') {
            e.preventDefault();
            let target = null;

            if (e.shiftKey) {
                if (e.target.previousSibling) {
                    target = e.target.previousSibling;
                } else {
                    target = e.target.nextSibling;
                }
            } else {
                if (e.target.nextSibling) {
                    target = e.target.nextSibling;
                } else {
                    target = e.target.previousSibling;
                }
            }
            target.focus();
        }
    }

    hideElementWheel() {
        // This is somehow the only option to trigger the showElementWheel
        this.setState({ test: false });
    }

    render() {
        return (
            <div
                className="element-add"
                style={{ opacity: this.props.invisible ? 0 : 1 }}
                onClick={() => this.setState(prevSate => ({ showElementWheel: !prevSate.showElementWheel }))}
            >
                <PlusIcon className="element-add__icon" />
                {this.state.showElementWheel && (
                    <React.Fragment>
                        <div className="element-add__wheel">
                            <button
                                autoFocus
                                className="element-add__circle"
                                onClick={this.handleAddCardClick.bind(this)}
                                onKeyDown={this.handleTabPress.bind(this)}
                                title="New element"
                            >
                                <ViewModuleIcon className="element-add__element-icon" />
                            </button>
                            <button
                                className="element-add__circle"
                                onClick={this.handleAddKnowledgeClick.bind(this)}
                                onKeyDown={this.handleTabPress.bind(this)}
                                title="New knowledge-element"
                            >
                                <FileIcon className="element-add__element-icon" />
                            </button>
                        </div>
                        <div
                            className="element-add__close-layer"
                            onClick={() => {
                                this.hideElementWheel();
                            }}
                        />
                    </React.Fragment>
                )}
            </div>
        );
    }
}

3 个答案:

答案 0 :(得分:3)

通过编写onClick={this.setState({showStuff: false})},您实际上是在渲染按钮后立即调用setState

您要给函数提供对onClick的引用,而不是在渲染时立即调用它。

<button onClick={() => this.setState({showStuff: false})} />

如果您的按钮位于您不想在同一点击上运行的带有点击侦听器的另一个元素内,则必须确保click事件不会传播到父对象。

<button
  onClick={(event) => {
    event.stopPropagation();
    this.setState({showStuff: false});
  }}
/>

答案 1 :(得分:0)

实际上onClick属性需要一个函数,您已经在提供函数调用,因此setState将在每次呈现组件时调用,而不是在单击时调用。

尝试一下:

<button onClick={() => this.setState({showStuff: false})} />

应该表现出预期的效果:)

答案 2 :(得分:0)

当我更新showStuff true时,效果很好(请参见下面的更新代码。)。我的猜测是应该设置showStuff的代码:true无法正常工作。我还在按钮中添加了一些文字。

import React from 'react'
import ReactDOM from 'react-dom'
class Componenent extends React.Component {
    constructor(props) {
      super(props);
      this.state = {showStuff: true};
    }

    render() {
      return(
        <div>

          {this.state.showStuff && (
            <button onClick={() => this.setState({showStuff: false})} > This is a button</button>
          )}
        </div>
      );
    }
  }

ReactDOM.render(<Componenent />, 
                document.getElementById('root')
);
  

点击之前

enter image description here

  

点击后

enter image description here