javascript - 当从依赖于要渲染的状态的元素调用 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>
);
}
}
解决方案
通过编写onClick={this.setState({showStuff: false})}
,您实际上setState
是在呈现按钮后立即调用。
你想给一个函数引用onClick
,而不是在渲染时立即调用它。
<button onClick={() => this.setState({showStuff: false})} />
如果您的按钮位于另一个带有单击侦听器的元素内,而您不想在同一次单击时运行,则必须确保单击事件不会传播到父级。
<button
onClick={(event) => {
event.stopPropagation();
this.setState({showStuff: false});
}}
/>