首页 > 解决方案 > 当从依赖于要渲染的状态的元素调用 setState() 时,不会更改状态

问题描述

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

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功能(回调被执行),但状态不会更新。


我的猜测是它不会更新,因为该函数是由一个直接依赖于 state 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>
        );
    }
}

标签: javascriptreactjsstate

解决方案


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

你想给一个函数引用onClick,而不是在渲染时立即调用它。

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

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

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

推荐阅读