react 无状态组件使用_如何在React中使用和操纵状态

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-clockch04

"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没有任何值。 与道具不同,州不是在父级上设置的。 我们不能setStaterender()或者,因为它会创建一个圆形(的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()仅更新您传递它的状态(部分或合并,而不是完全替换)。 并不是每次都替换整个状态对象。 如果您具有三个状态,然后更改一个,则其他两个保持不变。 在下面的示例中, userEmailuserId将保持不变:

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访问状态对象。 如果您还记得的话,我们用大括号( {} )输出值; 因此,要在视图中声明状态属性( renderreturn语句),请应用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 )值以备后用。 通常,我们会看到变量selfthat_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所示。它创建了一个内联样式,而不是类。 元素及其新的内联样式与滴答作响的时间保持不变(蓝色)。

React将时间更新为文本,而不是div元素

图3:React用文本而不是div元素更新时间(手动添加的颜色:蓝色)

React只会更新内部HTML(第二个<div>容器的内容)。 <div>此页面上的所有其他元素保持不变 。 整齐。 ;-)

状态和属性

状态和属性都是类的属性,这意味着它们是this.statethis.props 。 那是唯一的相似! 属性和状态之间的主要区别之一是前者是不可变的,而后者是可变的。

属性和状态之间的另一个区别是,当我们在组件本身而不是其父级中定义状态时,我们从父组件传递属性。 这里的原理是,您只能从父级更改属性的值,而不能更改组件本身。 属性在创建时确定视图,然后保持不变(它们不变)。 另一方面,状态由对象本身设置和更新。

道具和状态有不同的用途,但是两者都可以作为组件类的属性来访问,并且都可以帮助开发人员以不同的表示形式(视图)组成组件。 当涉及组件生命周期时,道具和状态之间存在许多差异。 考虑道具和状态作为产生不同输出的功能的输入。 这些输出是视图。 每个道具和状态集可以具有不同的UI(视图)(图4)。

道具和状态的新值可以更改UI

图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>

在无状态组件中,我们不能有状态,但是可以有两个属性: propTypesdefaultProps 。 我们将它们设置在对象上:

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 无状态组件使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值