11、React系列之--state状态

版权声明:本文为博主原创文章,未经博主允许不得转载。

PS:转载请注明出处
作者:TigerChain
地址:http://www.jianshu.com/p/44a787904d9b
本文出自TigerChain简书

React 教程系列

教程简介

  • 1、阅读对象

    本篇教程适合新手阅读,老手直接略过

  • 2、教程难度

    初级

注:本篇文章采用ES6的写法,以后不没有特别说明都使用ES6+yarn+webpack去写demo

1、什么是state

我们都说React是一个状态机,体现是什么地方呢,就是体现在state上,通过与用户的交互,实现不同的状态,然后去渲染UI,这样就让用户的数据和界面保持一致了。state是组件的私有属性

在React中,更新组件的state,结果就会重新渲染用户界面(不需要操作DOM),一句话就是说,用户的界面会随着状态的改变而改变。

PS: state 只能在本组件中去初始化,并且 state 只能被本组件去修改和仿问,不能被外部仿问和修改,所以也可以说,state 是组件私有的。

2、state的使用方法

1、初始化state

对于state的使用方法,在ES5中和Es6中使用是不一样的,我们以ES6为例,在组件的构方法中如下设置即可,设置一个认的state属性

constructor(props) { super(props); this.state={ key:value,... } }

2、更新state

更新state调用以下方法

this.setState({ key:value }) ;

千万不要这么干,this.state.key = XXX ; 这样不会重新渲染界面

注意:setState()是异步的,也就是你调用了setState()之后,React就开始准备去更新了,中间计算会可能会有一定的延时。

PS:如果调用了setState()方法以后,就会调用render()方法,也就是前面说的,用户的界面会随着状态的改变而改变。

3、调用diff算法

这一步是在2步的基础上的,setState()会触发diff算法最终确定是否要更新。

3、什么样的组件应该有state

应该说大部分的组件根据props来取得数据并渲染,但是用户输入,请求服务器,操作数据库等情况下就需要state了.官方的建议是尽量构建无状态的组件,是为了提高性能,减少渲染次数,做法就是构建几个无状态的组件,在这之上构建一个有状态和用户和服务交互 ,然后通过props来传递给无状态的组件。

使用state要注意三个事情

  • 1、不要直接的修改state
反例  请不要这样做
this.state.comment = 'Hello';

正确的做法是使用setState()方法

正确的做法
this.setState({ comment:'Hello',})
  • 2、状态更新是异步的

用官方的话说就是this.props和this.state更新的时候可能是异步的,所以你不应该依靠他们的值来计算下一个状态

以下是错误的例子

反例
this.setState({
  counter: this.state.counter + this.props.increment,});

正确的例子

正确的例子
this.setState((prevstate,props) => ({ counter: prevstate.counter + props.increment }))
  • 3、state的更新是合并后的

当你调用 setState() ,React会合并你提供的对象到当前的 state 里

比如:你的state可能包含几个独立的变量

constructor(props) { super(props); this.state = { posts: [],comments: [] };
  }

然后你可以通过调用setState()来独立的更新他们

componentDidMount() {
    fetchPosts().then(response => {
      this.setState({
        posts: response.posts
      });
    });

    fetchComments().then(response => {
      this.setState({
        comments: response.comments
      });
    });
  }

这个合并是浅合并,所以 this.setState({comments}) 会让 this.state.posts 完整,但是会完全替换掉 this.state.comments .

4、开始撸码

说一千,道一万,都是在理论级别,下面我们从0开始写一个简单的Demo来体验一下state。下面使用webpack+yarn来写一个demo。

在mac环境下,win下基本差不太多

1、在指定目录下新建state目录(命令行中操作)

mkdir state

2、使用yarn初始化项目(命令行中操作)

cd state
yarn init

在上面步骤一路回车即可

3、安装一些依赖(命令行中操作)

yarn add react react-dom webpack webpack-dev-server babel-core babel-loader babel-preset-react bable-preset-es2015 babel-preset-stage-0 --dev

然后回车,等待安装依赖,如果没有什么问题就可以在package.json中看到如下结果(红色框中)

以上就表明你的依赖安装成功了

4、在state根目录中新建.babelrc并输入以下内容

{
  "presets":["react","es2015","stage-0"] }

5、在state目录中分别新建app和public文件

6、在public中新建index.html并输入以下内容

<!DOCTYPE html>
<html lang="en">
<head>
  <Meta charset="UTF-8">
  <Meta name="viewport" content="width=device-width,initial-scale=1.0">
  <Meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Hello state</title>
</head>
<body>

  <div id="container"></div>
  <script src="bundle.js"></script>
</body>
</html>

7、在app目录中分别新建main.js和State.js

# main.js

import React from 'react' ;
import ReactDOM from 'react-dom' ;

import State from './State.js' ;
ReactDOM.render(
  <State />,document.getElementById('container')
) ;
# State.js
import React,{ Component,PropTypes } from 'react';

/** * 使用es6语法 定义一个State组件 */
export default class State extends Component {

  constructor(props) {
    super(props);
    this.state = { //初始化state
      countnum:0,};
  }

  /** * 点击事件方法 countnum+1 */
  _handlerEvent(){
    this.setState({
      countnum:this.state.countnum+1,})
  }
  render() {
    return (<div>
      {this._renderView()}
    </div>);
  }
  /** * 渲染一个button组件 */
  _renderView(){
    return(
      <div>
        <button onClick={this._handlerEvent.bind(this)}>点击{this.state.countnum}次</button>
      </div>
    );
  }
}

8、在state根目录中新建webpack.config.js并且输入以下内容,具体不清楚的可以看webpack这一节:http://www.jianshu.com/p/732c4d501668

module.exports = {
  entry:__dirname+'/app/main.js',output:{
    path:__dirname+'/public',filename:'bundle.js'
  },devServer:{
     contentBase: "./public",//本地服务器所加载的页面所在的目录
     historyApiFallback: true,//不跳转
     inline: true//实时刷新
   },module:{
    loaders:[
    //babel配置
    {
      test:/\.js$/,exclude: /node_modules/,loader: 'babel-loader'
    }
  ]
  }
}

9、在package.json中添加脚本(这不是必须的,但是可以方便调用)

图中的蓝色部分就是我们添加的脚本,即:

"scripts":{
    "start":"webpack-dev-server --progress --port 8888"
 }

10、在命令行直接运行 yarn start 如果没有什么问题,我们在浏览器上输入localhost:8888,会看到下面的结果

以上我们就完成了一个简单的state的例子,从这个例子中我们可以看到每当我点击按钮的时候组件的状态都会发生改变,计数就加1了,但是当我们调用了setState()方法到底有没有刷新界面呢?我们再测试一下

11、接着上面的例子我们继续修改State.js

从图中可知我们只是在原有的基础上添加了现代战争方法,用来测试render方法调用次数,然后保存,看结果(打开chrome的调试栏)

从图中我们可以清楚的看到,我们点击了多少次,render方法调用了多少次,所以说当调用了setState()方法就会重新渲染界面

到此为止,我们对state就有一个大体的认知了。下面我们来说一个无状态组件的例子。

5、无状态的组件

1、什么是无状态组件

无状态组件顾名思义就是没有状态的组件我们上面提到过无状态的组件,官方建议的是尽量让组件无状态化,但是肯定有一个地方要状态化的,一般做法是把要组合的组件无状态化,宿主组件有一个状态设置(一般情况下,也就是父组件更新子组件的时候,但是有时会反过来)

接下来我们构建一个无状态的组件,然后通过和用户交互(点击)的时候把状态通过props传给无状态的组件达到更新UI的目的,这么说可能有点绕我们看下面图来直观的理解一下

上图就是这一过程的一个代码,很好理解。

2、我们通过一个demo来直观感受一下

1、接着上面的例子,我们继续在app中分别新建PassstateOfprops.js和StateLess.js

# PassstateOfprops.js  此组件可以当作有状态的父组件

import React,PropTypes } from 'react';

import StateLess from './StateLess.js' ;

/** * 通过 props来传递state 达到更新组件的目的 */
export default class PassstateOfprops extends Component {
  constructor(props) {
    super(props);
    this.state = {  //初始化state
      list:[
        '111','222','333'
      ]
    }
  }

  /** * 渲染界面 */
  render() {
    return (<div>
      {this._renderButton()}
      {/* 将state通过props传递到无状态组件StateLess中 */}
      <StateLess datas={this.state.list}/>
    </div>);
  }

/** * 渲染一个button */
  _renderButton(){
    return(
      <div>
        <button onClick={this._handlerEvent.bind(this)}>改变值</button>
      </div>
    ) ;
  }

  /** * 点击事件 */
  _handlerEvent(){
    this.setState({
      list:['444','555','666']
    }) ;
  }
}
#  StateLess.js  定义一个无状态的组件

import React,PropTypes } from 'react';

/** * 定义一个无状态的组件 */
export default class StateLess extends Component {

  render() {
    return (<div>
      <ul>
        {
          this.props.datas.map(function (data) {
            return (
              <li>{data}</li>
            )
          })
        }
      </ul>

    </div>);
  }
}

2、修改main.js中代码

其中黄色部分就是我们修改的部分.

3、查看结果

我们看到通过props传递state到无状态的组件成功运行了,所以我们以后在项目中开发,尽量使用无状态化的组件,把这种思想和习惯从现在就养成。

本demo的地址

https://github.com/githubchen001/react-lesson/tree/master/lesson02/09-state

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom