react 无状态组件使用
以下摘自Azat Mardan的动手手册React Quickly的摘录,适用于任何想快速学习React.js的人。
在React中访问,使用和操纵状态时,了解您在做什么(以及为什么要这么做!)很重要。 在本文中,您将学习有关React中的状态以及如何使用它的知识。 我们还将讨论状态与道具之间的一些差异,以及如何使用“无状态”组件。 但是在深入研究所有这些之前,为了使用状态,我们需要知道如何访问值,更新它们以及如何设置初始值。 让我们开始访问React组件中的状态。
可以在本书的GitHub存储库的ch04文件夹中找到本文示例的源代码。
进入国
状态对象是组件的属性,可以使用this
引用(例如this.state.name
进行访问。 我们可以使用花括号{}
访问和打印JSX中的变量。 同样,我们可以在render()
内部渲染this.state
(作为任何其他变量或自定义组件类的属性render()
。 例如, {this.state.inputFieldValue}
。 这种语法就像使用this.props.name
访问属性。
让我们继续尝试实现时钟(图1)。 我们的目标是拥有一个自包含的组件类,任何人都可以导入并在其应用程序中使用它们,而不必跳过箍。 时钟必须呈现当前时间。
图1:时钟组件以数字格式显示当前时间,每秒更新一次
Clock项目的结构如下:
/clock
- index.html
/jsx
- script.jsx
- clock.jsx
/js
- script.js
- clock.js
- react-15.0.2.js
- react-dom-15.0.2.js
我将Babel CLI与手表-w
和目录标志-d
以将所有源JSX文件从clock/jsx
到目标文件夹clock/js
并在更改时重新编译。 此外,我已经保存的命令作为新公共管理脚本在我package.json
文件中的父文件夹名为ch04
才能运行npm run build-clock
从ch04
:
"scripts": {
"build-clock": "./node_modules/.bin/babel clock/jsx -d clock/js -w"
},
显然,时间总是在变化(无论好坏)。 因此,我们需要使用state更新视图。 我们将其命名为currentTime
并尝试呈现此状态,如清单1所示。
class Clock extends React.Component {
render() {
return <div>{this.state.currentTime}</div>
}
}
ReactDOM.render(
<Clock />,
document.getElementById('content')
)
清单1:JSX中的呈现状态
如果运行此命令,则会出现以下错误: Uncaught TypeError: Cannot read property 'currentTime' of null
。 通常,JavaScript错误消息对于溺水的人就像一杯凉水一样有用。 在这种情况下,JavaScript为我们提供了有用的错误消息是很好的。 这意味着我们对currentTime
没有任何值。 与道具不同,州不是在父级上设置的。 我们不能setState
在render()
或者,因为它会创建一个圆形(的setState>渲染>的setState ...)循环,在这种情况下,阵营将抛出一个错误。
设定初始状态
您已经看到,在render()
使用状态数据之前,我们必须对其进行初始化。 要设置初始状态, this.state
在ES6类React.Component
语法的构造函数中使用React.Component
。 不要忘记调用带有属性的super()
,否则父级( React.Component
)中的逻辑将无法正常工作。
class MyFancyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {...}
}
render() {
...
}
}
设置初始状态时,开发人员可以添加其他逻辑。 例如,我们可以使用new Date()
设置currentTime
的值。 我们甚至可以使用toLocaleString()
在用户位置获取正确的日期和时间格式:
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = {currentTime: (new Date()).toLocaleString()}
}
...
}
清单2:时钟组件构造器(ch04 / clock)
this.state
的值必须是一个对象。 由于ES6速查表中有信息,因此我们不会涉及到ES6 constructor()
的详细信息。 要点是,与其他OOP语言一样,在创建此类的实例时会调用constructor()
。 构造方法的名称必须为constructor
。 将其视为ES6约定。 此外, 如果创建一个constructor()
方法,则几乎总是需要在其内部调用super()
,否则将不会执行父级的构造函数。 另一方面,如果您未定义constructor()
方法,则假定调用了super()
。
类属性
希望TC39(ECMAScript标准的支持者)将在将来的ECMAScript版本中为类语法添加属性! 这样,开发人员不仅可以在构造函数中,而且可以在类的主体中设置状态:
class Clock extends React.Component {
state = {
...
}
}
该提案称为类实例字段或类属性 ,但自2016年7月起,仅适用于编译器:Babel,Traceur或TypeScript,这意味着没有浏览器将本机运行此功能。 在ECMAScript兼容性表中检查类属性的当前兼容性。
在这里, curentTime
是一个任意名称,稍后在访问和更新此状态时我们需要使用相同的名称。 如果以后要使用此名称进行引用,则可以使用任意方式命名您的州。
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
状态对象可以具有嵌套对象或数组。 看下面的示例,在其中向状态添加书本数组:
class Content extends React.Component {
constructor(props) {
super(props)
this.state = {
githubName: 'azat-co',
books: [
'pro express.js',
'practical node.js',
'rapid prototyping with js'
]
}
}
render() {
...
}
}
从此类创建React元素时,一次将调用一次constructor()
方法。 这样,我们可以在constructor()
方法中使用this.state
直接设置状态。 避免在其他任何地方直接使用this.state = ...
设置和更新状态,因为这可能会导致意想不到的后果。
使用React自己的
createClass()
方法来定义组件,您将需要使用getInitialState()
。
这只会使我们获得第一个价值,而这个价值很快就会过时。 一秒钟 不显示当前时间的时钟有什么意义? 幸运的是,有一种方法可以更新状态。
更新状态
我们使用this.setState(data, callback)
类方法更改状态。 调用此方法时,React将数据与当前状态合并并调用render()
。 之后,React调用callback
。
在setState()
具有回调很重要,因为这些方法是异步工作的。 如果您依赖于新状态,则可以使用回调来确保此新状态可用。 如果您依靠新状态而不等待setState()
完成其工作,即与异步操作同步工作,那么当状态仍然是旧状态时,您可能会遇到错误。
我们从状态渲染了时间,还设置了初始状态,但是我们需要每秒更新一次时间,对吗? 我们可以使用浏览器计时器函数setInterval()
,每隔n毫秒执行一次状态更新。 setInterval()
方法实际上在所有现代浏览器中都以全局方式实现,这意味着开发人员可以在不使用任何库或前缀的情况下使用它。
setInterval(()=>{
console.log('Updating time...')
this.setState({
currentTime: (new Date()).toLocaleString()
})
}, 1000)
要启动时钟,我们需要调用一次setInterval()
。 我们可以创建一个方法launchClock()
来做到这一点。 我们将在构造函数中调用launchClock()
。 最终的Clock可能类似于清单3所示。
清单3:使用React状态和setInterval()
实现时钟(ch04 / clock / jsx / clock.jsx)。
class Clock extends React.Component {
constructor(props) {
super(props)
this.launchClock() <1>
this.state = {
currentTime: (new Date()).toLocaleString() <2>
}
}
launchClock() {
setInterval(()=>{
console.log('Updating time...')
this.setState({
currentTime: (new Date()).toLocaleString() <3>
})
}, 1000) <4>
}
render() {
console.log('Rendering Clock...')
return <div>{this.state.currentTime}</div> <5>
}
}
<1>触发launchClock()
<2>将初始状态设置为当前时间
<3>每秒以当前时间更新状态
<4>绑定上下文以引用组件实例
<5>渲染状态
您可以在任何地方使用setState()
,不仅可以在launchClock()
(由constructor
调用)中使用,如示例所示。 通常,从事件处理程序中调用setState()
或将其作为传入数据或数据更新的回调。
这样在代码中更改状态值,例如
this.state.name= 'new name'
不会有任何好处。 它不会触发我们想要的重新渲染和可能的真实DOM更新。 在大多数情况下,不使用setState
直接更改状态是一种反模式,应避免使用。
重要的是要注意, setState()
仅更新您传递它的状态(部分或合并,而不是完全替换)。 并不是每次都替换整个状态对象。 如果您具有三个状态,然后更改一个,则其他两个保持不变。 在下面的示例中, userEmail
和userId
将保持不变:
constructor(props) {
super(props)
this.state = {
userName: 'Azat Mardan',
userEmail: 'hi@azat.co',
userId: 3967
}
}
updateValues() {
this.setState({userName: 'Azat'})
}
如果您打算更新所有三个状态,则需要通过将这些状态的新值传递给setState()
来显式地进行操作。 有时在旧的React代码中有时会看到的另一个方法是this.replaceState()
方法,但该方法不再起作用并且已被弃用。 从名称可以猜到,它用其所有属性替换了整个状态对象。
请记住, setState()
会触发render()
。 在大多数情况下都可以使用。 在某些this.forceUpdate()
情况下,当代码依赖于外部数据时,可以使用this.forceUpdate()
触发重新渲染,但是应避免这种方法,因为它依赖于外部数据而不是状态,从而使组件更加脆弱。并取决于外部因素(紧密耦合)。
如前所述,您可以使用this.state
访问状态对象。 如果您还记得的话,我们用大括号( {}
)输出值; 因此,要在视图中声明状态属性( render
的return
语句),请应用this.state.NAME
。
当您在视图中使用状态数据时(例如,将if/else
作为属性值或作为孩子的属性值打印),然后为setState()
新值,就会发生React setState()
。 繁荣! React为您更新了HTML。 您可以在DevTools控制台中观察它。 它应该显示更新……然后渲染的周期……而且,最好的部分是,仅影响最小的DOM元素。
用JavaScript绑定
在JavaScript中, this
会根据调用函数的位置来改变(更改)其值。 为了确保this
指的是我们的组件类,我们需要的功能结合到适当的范围内(这个值是我们的组件类)。
如果您在此处使用ES6 + / ES2015 +,则可以使用胖箭头函数语法创建具有自动绑定功能的函数:
setInterval(()=>{
this.setState({
currentTime: (new Date()).toLocaleString()
})
}, 1000)
自动绑定意味着,脂肪箭头创建的功能将得到的电流值this
是在我们的例子中, Clock
。
手动方法是在闭包上使用bind(this)
方法:
function() {...}.bind(this)
或为我们的时钟:
setInterval(function(){
this.setState({
currentTime: (new Date()).toLocaleString()
})
}.bind(this), 1000)
这种行为不是React独有的。 this
关键字在函数的闭包内部发生了变化,我们需要绑定它或保存上下文( this
)值以备后用。 通常,我们会看到变量self
, that
或_this
用来保存原始this
的值。 你们中的大多数人可能已经看到如下语句:
var that = this
var _this = this
var self = this
这个想法很简单; 您创建一个变量并在闭包中使用它,而不是引用this
。 新变量将不是副本,而是对原始this
值的引用。 这是我们的setInterval()
:
var _this = this
setInterval(function(){
_this.setState({
currentTime: (new Date()).toLocaleString()
})
}, 1000)
我们有了时钟,它可以正常工作(图2)。 ada!
图2:时钟在滴答作响
我们继续前的一件事。 您可以看到React如何重用相同的DOM <div>
元素,并且仅更改其中的文本。 继续并使用DevTools修改此元素CSS。 我添加了一种样式,使文本变为蓝色( color: blue
),如图3所示。它创建了一个内联样式,而不是类。 元素及其新的内联样式与滴答作响的时间保持不变(蓝色)。
图3:React用文本而不是div元素更新时间(手动添加的颜色:蓝色)
React只会更新内部HTML(第二个<div>
容器的内容)。 <div>
和此页面上的所有其他元素保持不变 。 整齐。 ;-)
状态和属性
状态和属性都是类的属性,这意味着它们是this.state
和this.props
。 那是唯一的相似! 属性和状态之间的主要区别之一是前者是不可变的,而后者是可变的。
属性和状态之间的另一个区别是,当我们在组件本身而不是其父级中定义状态时,我们从父组件传递属性。 这里的原理是,您只能从父级更改属性的值,而不能更改组件本身。 属性在创建时确定视图,然后保持不变(它们不变)。 另一方面,状态由对象本身设置和更新。
道具和状态有不同的用途,但是两者都可以作为组件类的属性来访问,并且都可以帮助开发人员以不同的表示形式(视图)组成组件。 当涉及组件生命周期时,道具和状态之间存在许多差异。 考虑道具和状态作为产生不同输出的功能的输入。 这些输出是视图。 每个道具和状态集可以具有不同的UI(视图)(图4)。
图4:属性和状态的新值可以更改UI,但是对于属性,新值来自父级,状态来自组件本身
并非所有组件都需要具有状态。 让我们看一下如何在无状态组件中使用属性。
无状态组件
无状态组件的概念是没有状态,没有组件或任何其他React生命周期事件/方法的组件。 无状态组件的目的是呈现视图。 它唯一能做的就是获取属性并对其进行处理—一个具有输入(属性)和输出(UI元素)的简单函数。
使用无状态组件的好处是它们是可预测的,因为我们只有一个输入可以确定输出。 可预测性意味着它们更易于理解,维护和调试。 实际上,无状态是最需要的React做法-您使用的无状态组件越多,它们的“有状态”越少,那就更好。
这个Hello World脚本是无状态组件的一个很好的例子(清单4):
class HelloWorld extends React.Component {
render() {
return <h1 {...this.props}>Hello {this.props.frameworkName} World!!!</h1>
}
}
清单4(ch03 / hello-js-world-jsx / jsx / script.jsx)
为了为无状态组件提供更小的语法,React为我们提供了一种函数样式。 我们创建一个将属性作为参数并返回视图的函数。 无状态组件的呈现方式与其他任何组件一样。 例如,可以将HelloWorld组件重写为返回<h1>
的函数:
const HelloWorld = function(props){
return <h1 {...props}>Hello {props.frameworkName} world!!!</h1>
}
注意:是的。 您可以将ES6 + / ES2015 +箭头功能用于无状态组件。 以下代码段与上面的代码段类似(返回值也可以省略,但我喜欢这样):
const HelloWorld = (props)=>{
return <h1 {...props}>Hello {props.frameworkName} world!!!</h1>
}
可以看出,开发人员还可以在不需要状态时将函数定义为React组件。 要创建无状态组件,只需将其定义为一个函数。 另一个示例,其中Link
是无状态组件:
function Link (props) {
return <a href={props.href} target="_blank" className="btn btn-primary">{props.text}</a>
}
ReactDOM.render(
<Link text='Buy React Quickly' href='https://www.manning.com/books/react-quickly'/>,
document.getElementById('content')
)
不需要自动绑定,但是为了简洁起见,我们可以使用胖箭头函数语法(当只有一条语句时,表示法可以是单行的):
const Link = props=> <a href={props.href} target="_blank" className="btn btn-primary">{props.text}</a>
在无状态组件中,我们不能有状态,但是可以有两个属性: propTypes
和defaultProps
。 我们将它们设置在对象上:
function Link (props) {
return <a href={props.href} target="_blank" className="btn btn-primary">{props.text}</a>
}
Link.propTypes = {...}
Link.defaultProps = {...}
我们也不能将引用( refs
)与无状态函数一起使用。 如果需要使用refs
,则可以将无状态组件包装在常规的React组件中。
结论
在本文中,我介绍了React中的状态并演示了如何使用它。 我研究了状态和道具之间的一些差异,以及如何使用所谓的无状态组件。
到此为止—希望这可以使您更好地了解在React中使用状态。 有关React及其无数用途的更多信息,请查阅本书: React Quickly 。
翻译自: https://www.sitepoint.com/work-with-and-manipulate-state-in-react/
react 无状态组件使用