反应中没有构造函数的初始化状态
Posted
技术标签:
【中文标题】反应中没有构造函数的初始化状态【英文标题】:init state without constructor in react 【发布时间】:2017-08-17 01:35:52 【问题描述】:import React, Component from 'react';
class Counter extends Component
state = value: 0 ;
increment = () =>
this.setState(prevState => (
value: prevState.value + 1
));
;
decrement = () =>
this.setState(prevState => (
value: prevState.value - 1
));
;
render()
return (
<div>
this.state.value
<button onClick=this.increment>+</button>
<button onClick=this.decrement>-</button>
</div>
)
通常我看到的是,如果他使用 es6 类,人们会在构造函数中执行 this.state。如果他不是,他可能会使用 getinitialstate 函数设置状态。但是上面的代码(是的,它是一个工作代码),两者都没有使用。我有 2 个问题,这里的状态是什么?那是局部变量吗?如果是,为什么它没有const
? prevState 来自哪里?为什么在 setState 中使用箭头函数?做this.setState(value:'something')
不容易吗?
【问题讨论】:
【参考方案1】:我有两个问题,这里的
state
是什么?
实例属性,例如在构造函数中设置this.state = value: 0;
。它使用的是目前处于第 2 阶段的 Public Class Fields proposal。(increment
和 decrement
也是如此,它们是实例字段,其值是箭头函数,因此它们关闭 this
。)
这是一个局部变量吗?
没有。
prevState 来自哪里?为什么在 setState 中使用箭头函数?做 this.setState(value:'something') 不是很容易吗?
来自the documentation:
React 可以将多个
setState()
调用批处理到单个更新中以提高性能。由于
this.props
和this.state
可能会异步更新,因此您不应依赖它们的值来计算下一个状态。例如,这段代码可能无法更新计数器:
// Wrong this.setState( counter: this.state.counter + this.props.increment, );
要修复它,请使用
setState()
的第二种形式,它接受函数而不是对象。该函数将接收先前的状态作为第一个参数,并将应用更新时的道具作为第二个参数:// Correct this.setState((prevState, props) => ( counter: prevState.counter + props.increment ));
...这正是引用的代码正在做的事情。这是错误的:
// Wrong
increment = () =>
this.setState(
value: this.state.value + 1
);
;
...因为它依赖于this.state
的状态,上面告诉我们不要这样做;所以引用的代码会这样做:
increment = () =>
this.setState(prevState => (
value: prevState.value + 1
));
;
这里证明了 React 可能以不明显的方式批量调用,以及为什么我们需要使用 setState
的回调版本:这里,我们有 increment
和 decrement
被调用两次 每次点击而不是一次(一次按按钮,一次按包含按钮的跨度)。单击+
一次 应该将计数器增加到2,因为increment
被调用了两次。但是因为我们没有使用 setState
的函数回调版本,所以它没有:其中一个对 increment
的调用变成了无操作,因为我们使用了一个陈旧的 this.state.value
值:
class Counter extends React.Component
state = value: 0 ;
increment = () =>
/*
this.setState(prevState => (
value: prevState.value + 1
));
*/
console.log("increment called, this.state.value = " + this.state.value);
this.setState(
value: this.state.value + 1
);
;
fooup = () =>
this.increment();
;
decrement = () =>
/*
this.setState(prevState => (
value: prevState.value - 1
));
*/
console.log("decrement called, this.state.value = " + this.state.value);
this.setState(
value: this.state.value - 1
);
;
foodown = () =>
this.decrement();
;
render()
return (
<div>
this.state.value
<span onClick=this.fooup>
<button onClick=this.increment>+</button>
</span>
<span onClick=this.foodown>
<button onClick=this.decrement>-</button>
</span>
</div>
)
ReactDOM.render(
<Counter />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
使用函数回调,它可以正常工作(没有对increment
的调用变为无操作):
class Counter extends React.Component
state = value: 0 ;
increment = () =>
this.setState(prevState =>
console.log("Incrementing, prevState.value = " + prevState.value);
return
value: prevState.value + 1
;
);
;
fooup = () =>
this.increment();
;
decrement = () =>
this.setState(prevState =>
console.log("Decrementing, prevState.value = " + prevState.value);
return
value: prevState.value - 1
;
);
;
foodown = () =>
this.decrement();
;
render()
return (
<div>
this.state.value
<span onClick=this.fooup>
<button onClick=this.increment>+</button>
</span>
<span onClick=this.foodown>
<button onClick=this.decrement>-</button>
</span>
</div>
)
ReactDOM.render(
<Counter />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
在这种情况下,当然,我们可以查看render
方法并说“嘿,increment
在单击期间会被调用两次,我最好使用回调版本的setState
。”但是,与其假设在确定下一个状态时使用this.state
是安全的,不如不 假设这一点。在一个复杂的组件中,使用 mutator 方法很容易以一种 mutator 方法的作者可能没有想到的方式。因此,React 作者的声明:
因为
this.props
和this.state
可能会异步更新,您不应依赖它们的值来计算下一个状态。
【讨论】:
我用了很多这个this.setState( value: this.state.value + 1 );
至少它有效,没有错。我没有遇到任何问题。
@JennyMok:“这没什么错”——是的,有。仅仅因为它还没有咬你,那并不意味着它没有任何问题。请参阅文档。 React 开发人员可能比我们更了解 React,并且不只是随心所欲地添加这种接受函数的表单。【参考方案2】:
关于问题 2,请参考 Dan 的精彩回答:Do I need to use setState(function) overload in this case?
不,它不是局部变量。和在构造函数中声明this.state
一样。
是的,在这种情况下你可以使用this.setState( value: this.state.value + 1 )
,结果是一样的。
但请注意,通过使用函数式setState
,您可以获得一些好处:
如果在外面声明 setState 函数可以重复使用:
const increment = prevState => (
value: prevState.value + 1
)
现在如果你有几个组件需要使用这个功能,你可以在任何地方导入和重用逻辑。
this.setState(increment)
React 压缩了几个 setState
并批量执行它们。这可能会导致一些意外行为。请看下面的例子:
http://codepen.io/CodinCat/pen/pebLaZ
add3 ()
this.setState( count: this.state.count + 1 )
this.setState( count: this.state.count + 1 )
this.setState( count: this.state.count + 1 )
执行这个函数count
只会加1
如果你使用函数式 setState:
http://codepen.io/CodinCat/pen/dvXmwX
const add = state => ( count: state.count + 1 )
this.setState(add)
this.setState(add)
this.setState(add)
count 会像你预期的那样 +3。
您可以在此处查看文档:https://facebook.github.io/react/docs/react-component.html#setstate
【讨论】:
“是的,在这种情况下你可以使用this.setState( value: this.state.value + 1 )
,结果是一样的。” 完全不正确,正如我在回答中指出的文档(你在你的结尾)很清楚地说。说“你可以使用 [it]”是完全错误的。
我是说OP的代码的increment
函数,只有1个setState语句,就可以了
不,不是。从文档中:“React 可以将多个 setState()
调用批处理到单个更新中以提高性能...”(我的重点)。因此,如果有多个对 increment
的调用,React 认为它可以批处理(即使从代码中看不出它可以批处理),它会;结果将是错误的。如果只是你故意反复调用setState
,你自己就能搞定。
当你在单个处理程序中多次调用 setState 时,React 只批处理 setState,所以如果你的函数只包含 1 个 setState 是安全的
请参阅我答案的结尾,以证明这不是真的。批处理可以以不明显的方式发生;在 mutator 中编写依赖于 this.state
的代码是一种糟糕的做法。同样,React 开发人员创建了 setState
的函数版本出于某种原因。【参考方案3】:
问题 1 的答案:
是的,你可以在没有构造函数的情况下为 React 类组件初始化状态:
class Counter extends Component
state = value: 0 ;
handleIncrement = () =>
this.setState(prevState => (
value: prevState.value + 1
));
;
handleDecrement = () =>
this.setState(prevState => (
value: prevState.value - 1
));
;
render()
return (
<div>
this.state.value
<button onClick=this.handleIncrement>+</button>
<button onClick=this.handleDecrement>-</button>
</div>
)
它使用类字段声明。您可以通过here 查看示例应用程序。你也可以在 React 函数组件中使用 state(没有构造函数),但是使用高阶组件或 render prop 组件。您可以通过here了解更多信息。
问题 2 的答案:在 React 中使用 this.setState
更新本地状态有两种方法。您可以使用对象或函数。如果您不依赖组件的道具或状态来更新其状态,那么该对象就可以了。如果你依赖 props 或 state,你应该使用这个函数,因为它在执行时总是有最新的 props 和 state。所有这一切都是因为this.setState()
是异步执行的,因此在使用对象而不是函数更新本地状态时,您可以使用陈旧状态或道具。你可以阅读更多关于它的信息here。
【讨论】:
对于您在组件的本地空间中初始化状态的方式...就在此处...,您能否获得对 props 的引用来初始化状态值,例如this.props.value
而不是比state = value: 0
即没有我想要的构造函数,但是:state = value: this.props.value - 可能不可能吧?以上是关于反应中没有构造函数的初始化状态的主要内容,如果未能解决你的问题,请参考以下文章
使用构造函数与 state = 在反应组件中声明状态有啥区别?