setState 意外更新非状态属性



我不知道这是一个已知问题还是一个预期的功能,但我发现了一个有趣的问题。

所以我们都知道,如果我们想在 React 中渲染一个反应式值,我们必须把这个值放在 state 中并使用 setState:

constructor() {
super();
this.state = { counter: 0 }
this.incrementButtonListener = (e) => {
e.preventDefault();
this.setState(prevState => ({ counter: prevState.counter + 1 }));
};
}
render() {
return (
<div>
<h1>{this.state.counter}</h1>
// When clicked, counter increments by 1 and re-renders
<button onChange={this.incrementButtonListener}>Increment</button> 
</div>
)
}

但是如果我们counter作为字段属性,render(( 只会在创建组件时捕获counter的快照,即使counter递增,结果也不会在 render(( 中被动显示:

constructor() {
super();
this.counter = 0;
this.incrementButtonListener = (e) => {
e.preventDefault();
this.counter++;
};
}
render() {
return (
<div>
<h1>{this.counter}</h1>
// When clicked, counter increments by 1 but the difference is NOT rendered
<button onChange={this.incrementButtonListener}>Increment</button> 
</div>
)
}

右?基本的东西。

但是,当我尝试摆弄此代码时,有一个有趣的事件。我们将计数器作为字段属性保留,其他所有内容都完好无损。唯一的区别是,在incrementButtonListener中,我将在someStateProperty上添加一个setState

constructor() {
super();
this.counter = 0;
this.incrementButtonListener = (e) => {
e.preventDefault();
this.counter++;
/*-------------------------------ADD THIS*/
this.setState({});
// You have to pass an object, even if it's empty. this.setState() won't work.
/*-----------------------------------------*/
};
}
render() {
return (
<div>
<h1>{this.counter}</h1>
// Surprise surprise, now this.counter will update as if it was in the state! 
<button onChange={this.incrementButtonListener}>Increment</button> 
</div>
)
}

这一次,this.counter 更新,就好像它处于状态一样!

所以我的假设是,每次调用 setState 时(即使使用空对象作为参数(,render(( 会再次运行,this.counter将被重新计算并因此递增。当然,它不会像国有财产那样 100% 被动。但是,在这个用例中,this.counter唯一会改变的时间是当我单击Increment按钮时。因此,如果我在侦听器中放置一个 setState,它将像this.counter处于该状态一样工作。

现在,我不确定这是一种可接受的行为还是只是一个意外的黑客,以及我是否应该使用它。 有人可以帮我详细说明一下吗?

如果您想查看实际行为,这是一个小提琴。您可以注释掉第 7 行中的this.setState({})位以查看差异。

由于您没有拦截状态更改,因此会导致重新渲染,这反过来又会导致使用递增的实例属性。这是设计使然。对 React 状态的任何更改都将导致组件重新渲染,除非您使用生命周期钩子来控制是否应该发生这种情况。

请参阅 https://reactjs.org/docs/react-component.html#shouldcomponentupdate

React 使用this.statesetState,因为它有自己的目的。

通过使用 React 状态机制,我们得到了诸如冗长、类似函数的编程风格之类的好处,该函数应该是纯的,不会改变数据。

每次调用setState时,它都会使用新值来改变现有值并使行为不可预测。

此外,您还可以通过利用检查其中的 props/状态shouldComponentUpdate来防止组件重新渲染或更新。

在您的应用程序变得复杂之后,像Redux这样的库将来可以节省您的屁股。对于更简单的组件或应用程序,React 状态就足够了。

延伸阅读:

https://reactjs.org/docs/faq-state.html

https://reactjs.org/docs/state-and-lifecycle.html

https://reactjs.org/docs/react-component.html#shouldcomponentupdate

https://spin.atomicobject.com/2017/06/07/react-state-vs-redux-state/

您可以在 forcrUpdate 上替换 setState。 如果你的团队使用装饰风格的代码,那么永远不要使用 setState({}( 或 forceUpdate((。 React 的作者建议像纯函数一样制作组件。

class Hello extends React.Component {
state = { counter: 0 };
increment = () => {
const { counter } = this.state;
this.setState({ counter: counter + 1 });
};
decrement = () => {
const { counter } = this.state;
this.setState({ counter: counter - 1 });
};
render() {
const { counter } = this.state;
return (
<div>
<h1>{counter}</h1>
<p>
<button onClick={this.increment}>Increment</button>
</p>
<p>
<button onClick={this.decrement}>Decrement</button>
</p>
</div>
);
}
}
ReactDOM.render(<Hello />, document.getElementById("container"));

我希望这能帮助你做出反应! 如果您有任何问题,请告诉我:)

最新更新