zoukankan      html  css  js  c++  java
  • react总结之组件(props&state)

      组件从概念上来看就像JS中的一个函数,它可以接收任意的输入值(称之为props),并返回一个需要在页面上展示的React元素。我们可以将UI切分成几个不同的,独立的,可复用的部分,进行单个部分即单个组件的构建,后面进行整合展示就可。

      一、函数组件和类组件

      组件的名称必须是大写开头,这样可以在使用时和html标签区分开来。函数组件的创建是定义一个首字母大写的函数,这个函数返回jsx,jsx它是依赖React,所以组件内部必须要引入React。在使用组件时,里面写的行内属性都是自定义属性,我们在函数的内部通过props进行接收。组件需要返回一个并且只能返回一个React根元素,所以有多个组件时要用空标签或者是div标签进行包裹。

    import React from 'react';
    import ReactDOM from 'react-dom';
    
    let str = '我是天空里的一片云'
    //普通函数
    function Div(props) {
      // 在组件上使用的行内属性都是自定义属性
      return <h3>我的名字是:{props.name},年龄是:{props.age}</h3>
    }
    //箭头函数
    let H3 = (props) => {
      // 在html标签上使用的行内属性都是react规定的
      return <h3 style={{color:props.style}}>{str}</h3>
    }
    
    ReactDOM.render(<>
      <Div name="davina" age={20} />
      <H3  style="ligthblue"></H3>
    </>, 
    document.querySelector('#root'))

      同函数组件一样,类组件首字母也需大写,它继承自React.Component。类组件有自己的this和生命周期。如下所示,我们创建了一个Welcome类,它里面必须要有render函数,会默认调用render方法,并且返回一个能被渲染的结果,这个返回结果就是虚拟DOM,即React元素。在return的React元素有多个时,要有标签进行包裹,不然会报错。

      类组件也是先进行属性对象的收集,像下面的<Welcome {...data} />中{ name: 'davina', age: 20 }就会作为Welcome类组件的props属性对象进行传入,传入后会把属性对象传递给构造函数,并得到类的实例。在props初始化完成后,this.props变量就保存了props属性对象的地址,后面我们在调用render函数时,可以通过this.props访问数据。组件的props一般来源于默认属性或者是由父组件的state内部数据传递而来,基于react是单向数据流,所以在组件内部props是只读的不能修改。虽然可以通过修改父组件的state方式进行修改,但是不建议它仍旧遵循的是props属性对象不可更改这一规则。

    import React from 'react';
    import ReactDOM from 'react-dom';
    
    //Wlecome类继承于React.Component这个类
    class Welcome extends React.Component {
      //在class声明的类中有一个规定:写了constructor就必须要写super()
      // constructor(props) {
      // super()它相当于是call继承,其实就是继承的那个类的函数体本身,在这里指的就是React.Component
      // super(props) //将props挂载在this上
      // }
      render() {
        //可以通过this.props调用到属性
        console.log( this.props);
        return <h3>我的名字是:{this.props.name},年龄是:{this.props.age}</h3>
      }
    }
    let data = { name: 'davina', age: 20 }
    ReactDOM.render(<Welcome {...data} />, document.querySelector('#root'))

      二、state

      react中的组件只有两大数据源,一个属性props,二是状态state。上文中我们说了props,下面我们来看一下state。每个组件都有自己独立的内部数据,在类组件中state内部数据是放在构造函数中作为私有属性来定义的。

      当我们要修改state时,因为react不像vue那样对数据进行监听并且在数据变化时刷新视图,它的数据变化是通过重新调用render方法来更新视图。所以我们需要调用render函数。babel提供了一个可以自动调用render函数的API即setState()。我们利用setState可以触发视图的更新,也就是让render函数执行。换句话说,当我们调用了setState这个函数时,react会更新组件的state,并且重新调用render方法,再把render方法渲染的最新内容显示到页面上。react在更新组件的state时它并不会马上修改state,而是把它放到一个事件队列里面,当数据更数完后才会将新的state提取出来合并到旧的state中,所以只需要传入新的state需要修改的部分就可。随后再进行组件的更新操作。

    import React from 'react';
    import ReactDOM from 'react-dom';
    class App extends React.Component {
      constructor(props) {
        super(props)
        //在构造函数中这是唯一可以给this.state赋初始值的地方
        // 当前组件的私有属性
        this.state = {
          name: 'davina',
          date: new Date().toLocaleTimeString()
        }
      }
      //当组件渲染完成后会触发componentDidMount钩子函数
      componentDidMount() {
        //改变状态的方法:setState => 修改状态 重新render
        this.$timer = setInterval(() => {
          this.setState({ date: new Date().toLocaleTimeString() })
        }, 1000);
      }
      render() {
        //解构赋值
        let { name, date } = this.state;
        return <>
          <h3>我的名字是:{name}</h3>
          <h4> 现在的时间为:{date}</h4>
        </>
      }
    }
    ReactDOM.render(<App />, document.getElementById('root'))

      setState函数它是异步的,因为如果每次调用setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,这样效率会很低。再者如果同步的更新state,但是还没有执行render函数,那state和props不能保持同步和一致性,这样会在开发中导致很多问题,所以最好的办法就是获取到多个更新后批量的进行操作异步处理。

      但setState一定是异步的吗?不一定。更新数据时绝大多数情况下是异步操作。但在原生事件或者是计时器中setTimeout/setInterval这种react无法掌握的API会直接去更新state,可以立即得到最新的state它是一个同步的操作。 

      一般来说在组件生命周期或react合成事件中,setState是异步的,在setTimeout或者原生事件中setState是同步的。 

      我们可以使用以下方法强制让react不用异步操作。setState函数式用法即将一个回调函数传入setState方法中或者可以在setState更新后进行的逻辑封装到一个函数中作为第二个参数传给setState即setState(updater,[callback])还可以把需要在setState更新后进行逻辑放在生命周期的hook函数中通过以上的三种方法我们可以实现同步的操作。

    import React from 'react';
    import ReactDOM from 'react-dom';
    class App extends React.Component {
      //state的另一种写法,可以不用写constructor
      state = { count: 100, val: 20 }
      add() {
        this.setState(
          { count: this.state.count + 1 }, function () {
            console.log(this.state.count);//这时数据更新完就立即触发,它是一个同步操作
          }
        )
        console.log(this.state.count);//它是一个异步操作,得到的不是立即加1的值,它是旧的state 
      }
    
      componentDidMount() {
        //同步执行
        this.timer = setInterval(() => {
          this.setState({ val: this.state.val - 1 })
          console.log(this.state.val);
        }, 1000);
      }
    
      render() {
        let { count, val } = this.state;
        return <>
          <button onClick={this.add.bind(this)}>add</button>
          <h3>count的当前值是:{count}</h3>
    
          <h4>val的当前值是:{val}</h4>
        </>
      }
    }
    
    ReactDOM.render(<App />, document.getElementById('root'))
    import React, { Component } from 'react'
    export default class App extends Component {
        constructor(props) {
            super(props)
            this.state = {
                count: 0
            }
        }
        render() {
    
            return (
                <div>
                    <h3>当前计数是:{this.state.count}</h3>
                    <button onClick={e => this.incrementOne()}>clickOne</button>&nbsp;&nbsp;
                    <button onClick={e => this.incrementTwo()}>clickTwo</button>
    
                </div>
            )
        }
        incrementOne() {
            // 1.setState本身的合并 多次调用会产生合并
            this.setState({
                count: this.state.count + 1
            })
            this.setState({
                count: this.state.count + 1
            })
        }
        incrementTwo() {
            // 2.setState合并时进行累加,那么后面需要跟上一个函数
            //这个函数接收前一个状态值作为第一个参数,第二个参数是更新后的值
            this.setState((prevState, props) => {
                return {
                    count: prevState.count + 1
                }
            });
            this.setState((prevState, props) => {
                return {
                    count: prevState.count + 1
                }
            });
        }
    }

      三、生命周期

     当组件实例被创建并插入到DOM中时,它要经历以下几个步骤:

      1、Mounting(挂载阶段):组件第一次在DOM树中被渲染的过程

        1.1 constructor构造函数

        就像下面的代码中显示的一样,App类继承了React Component这个基类,constructor()它是用来做一些组件的初始化操作通过分配对象到this.state来初始化state,或者是进行事件处理中this的绑定 。需要注意的是在第一行要加上super(props)将父组件的props传递给子组件进行读取。

        constructor(props) {
            super(props);
            console.log(' mount阶段- 执行了组件的constructor方法')
            this.state = {
                count: 0,
            }
        }

        1.2 getDerivedStateFromProps(nextProps,prevState)

        这个函数它在render方法之前执行,它有两个参数:第一个参数为即将更新的props值,第二个参数为之前的state值。当它返回nulll时,不做任何其它的处理,当它返回一个对象时,更新state状态值。一般用的很少。

        1.3 render()

        根据组件的state和props,return一个react元素。它是一个纯函数,在类组件中其它的生命周期可以没有,但它是唯一必须存在的。组件渲染时会走到这个生命周期,展示什么组件都是由render()生命周期的返回值来决定的,但是它不负责组件的实际渲染工作。当shouldComponentUpdate()方法返回false时,render()不会被调用。

        1.4 componentDidMount()

        它是在组件挂载后即插入到DOM树中立即调用,只会被调用一次。我们可以把依赖于DOM的操作、发放网络请求、添加订阅事件、进行事件监听等操作放入其中。在这个生命周期中可以调用setState方法。

      2、Updating阶段(更新阶段):组件状态发生变化,重新更新渲染的过程

        2.1 shouldComponentUpdate(nextProps,nextState)

        它是组件准备更新之前调用的,可以控制组件是否进行更新,当它返回true时,执行render方法,组件更新,当它返回false,组件不更新。它包含两个参数,第一个是即将更新的props值,第二个是即将更新后的state值。shouldComponentUpdate()它可以用于性能的优化上,根据更新前后的props或者state进行判断。强调一点的是setState()不能用于shouldComponentUpdate中,不然会导致无限循环调用更新。

        2.2 getSnapshotBeforeUpdate(prevProps,prevState)

        它是在render后,即将对组件进行挂载时调用。getSnapshotBeforeUpdate()的返回值会传递给componentDidUpdate()。这个方法一般用的也比较少。

        2.3 componentDidUpdate(prevProps,prevState,snappshot)

        componentDidUpdate在更新发生之后立即被调用。这个生命周期在组件第一次渲染时不会触发。在这个生命周期中可以调用setState方法,但是这是有条件的,即它要包含在条件语句中。

      3、Unmounting(卸载阶段):组件从DOM树中被移除的过程

        3.1 componentWillUnmount() 它是卸载阶段唯一的方法,在组件即将被卸载或者是销毁时进行调用。 在这个生命周期中我们可以取消网络请求,清理定时器,移除事件监听等等。

  • 相关阅读:
    wcf常用的概念
    WebApi初探之路由配置
    NuGet Package Manager 实用命令
    WebApi初探之基本操作(CRUD)
    Excel操作类
    在Runbook中添加Checkpoint-workflow
    总结PowerShell的常用命令
    alt text 与 tooltip区别
    IFrame 获取内容
    WP8.1 实现Continuation程序(打开文件,保存文件等)
  • 原文地址:https://www.cnblogs.com/davina123/p/13615162.html
Copyright © 2011-2022 走看看