react系列---【react组件通信、生命周期、组件优化、错误边际处理、表单、HOC高阶组件】

1.props

1.props可以传递数据,子组件接收

父组件传数据

 <Child name={name} age={age} json={json} ></Child>

子组件接收

let {
  name,
  age,
  json: { x },
} = this.props;
2.取值
let {
  name,
  age,
  json: { x },
  change
} = this.props;
3.全部传递给子组件
 <ChildChild {...this.props}></ChildChild>
4.super(props)
constructor(props) {
    // super()为了继承了,有了super(),构造函数才会有this
    // super(props) 是为了在constructor中有this.props,否则是undefined
    super(props);

    console.log(this.props);
    this.state = {
      name: this.props.name,
    };
  }
5.组件调用嵌套的内容是this.props.children
<Child {...this.state}  change={(name)=>this.changeName(name)}>
  <div>天道酬勤</div>
</Child>

Child.jsx

{this.props.children}
6.传递方法

父组件

 <Child name={name} age={age} json={json} change={(e)=>this.changeName(e)}></Child>

子组件

 <button onClick={()=>this.props.change('貂蝉')}>貂蝉</button>
7.state和props的区别:
state:组件自己的状态;可以直接修改,但是需要通过setState()进行修改
props:父组件传递过来的数据;props是只读的,不能修改,如果要修改,需要子传父。

2.组件通信

父传子:父组件通过自定义属性传值,子组件通过props接收

父组件:

 <Goods goods={this.state.goods} ></Goods>

子组件:

import React from 'react'

export default function Child(props) {
    return (
        <div>
            {
                props.goods.map(item=>{
                    return (
                        <div key={item.id}>{item.name} <button onClick={()=>props.del(item.id)}>删除</button> </div>
                    )
                })
            }
        </div>
    )
}

子传父:父组件将方法通过属性传递,子组件通过props接收

父组件传递方法:

<Goods  del={(id)=>this.del(id)}></Goods>

子组件触发:

 <div key={item.id}>{item.name} <button onClick={()=>props.del(item.id)}>删除</button> </div>

3.生命周期

/* 初始期
constructor:初始化数据
static getDerivedStateFromProps()  渲染之前二次修改state
    1.constructor中需要有state初始值;
    2.返回一个新的state
    3. 
render() 渲染期:加载DOM节点
componentDidMount() 挂载完成:开请求、计时器、操作dom,给window和document加事件
*/

/* 更新期 :state
static getDerivedStateFromProps()  渲染之前二次修改state
    1.constructor中需要有state初始值;
    2.返回一个新的state
    3.如果不修改state,返回null
shouldComponentUpdate 判断要不要更新
    1.如果写了,没有返回值,会报错的
    2.return true ,正常更新
    3.return falsel, 就不会触发render
render() 渲染期:进行diff算法,计算出最优的更新方案,然后局部更新
componentDidUpdate() 更新完成
*/
/*销毁期
componentWillUnmount :销毁之前:清除计时器 清除window和document上的事件
*/

4.表单

1.受控组件

特点:
就地反馈,如:验证
禁用按钮 如:提交
执行特定的输入格式 如:身份证号、银行卡号
取值赋值
特征 取值 赋值
Input type="text" e.target.value value
Input type="radio" e.target.value checked
Input type="checkbox" e.target.value |e.target.checked checked
Select e.target.value Value
Textarea e.target.value Value
//修改user
  changeUser(e, key) {

    let value = e.target.value;

    if(key==="tel"){
        let {tel}=this.state.user
        if(tel.length<3&&value.length===3){
            value+=" "
        }
        if(tel.length<8&& value.length===8){
            value+=" "
        }
        if(value.length>13){
            value=value.slice(0,13)
        }
    }

    if (key === "hobby") {
      //取出原来的hobby,如果这次e.target.checked是true,就将e.target.value添加到hobby数组中;
      //如果是false,将e.target.value从hobby中删除
      let { hobby } = this.state.user;
      let val=parseInt(e.target.value)
        if(e.target.checked){
            hobby.push(val)
        }else{
            hobby.splice(hobby.indexOf(val),1)
        }
        value=hobby
    }
    //如果是isAgree
    if(key==="isAgree"){
        value=e.target.checked;
    }

    this.setState({
      user: {
        ...this.state.user,
        [key]: value,
      },
    });
  }

2.非受控组件

this.name=React.createRef()
 this.pass=React.createRef()
<div className="box">
  <h4>login3</h4>
  <div>账号:<input type="text" ref={this.name} /></div>
  <div>密码:<input type="text" ref={this.pass}/></div>
  <button onClick={()=>this.login()}>登录</button>
</div>
 //登录3
login(){
  console.log(this.name.current.value);
  console.log(this.pass.current.value);
}

3.对比受控和非受控组件

特征 受控组件 非受控组件
一次性检索(例如表单提交)
及时验证
有条件的禁用提交按钮
执行输入格式
一个数据的几个输入
动态输入

5.ref

1.获取DOM节点
constructor(){
  super()

  //1.创建ref对象
  this.div=React.createRef();


}
<div ref={this.div}></div>
this.div.current.innerHTML="嘿嘿嘿";
this.div.current.style.background="pink";
2.获取子组件的实例
  // 2.创建ref给子组件
  this.child=React.createRef()
<Child ref={this.child}></Child>
click(){
  this.child.current.changeName("王昭君")
}

6.组件优化

1.componentWillUnmount

清除计时器

2.shouldComponentUpdate

父组件状态的改变,也会默认引起子组件的重新渲染,为了减少不必要的渲染,我们可以借助shouldComponentUpdate来实现。

情景1:父组件有很多状态,子组件用了部分状态,父组件只要有任何一个数据变了,子组件默认都会更新;

我们想要子组件用到的数据变了,子组件才更。

import React, { Component } from "react";

export default class ShouldChild extends Component {
  //判断状态是否更新
  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.num === nextProps.num) {
      return false;
    }
    return true;
  }
  render() {
    console.log("child render is running  ...");
    let { num } = this.props;
    return (
      <div className="box">
        <h3>should child</h3>
        <div>num:{num}</div>
      </div>
    );
  }
}

3.PureComponent

场景:有一个父组件,有2个状态,date和num,date状态该组件自己使用,将num传递给子组件,此时我们希望只有num改变,子组件才再次重新渲染,除了上述的shouldComponentUpdate,我们也可以使用PureComponent来实现。

Component不会对数据进行比较,PureComponent将对propsstate进行浅比较。

import React, { PureComponent } from 'react';


class Child2 extends PureComponent {
    render() {
        console.log("child2 render is running... ")
        return (
            <div className="box">
                <h1>child2:{this.props.num}</h1>
            </div>
        );
    }
}

export default Child2;

[注意]:

1.PureComponent是浅比较,如果子组件接收的是对象或者数组,需要使用拷贝才能引起子组件的渲染;

push(){
      let { arr } = this.state;
      arr.push({
        id: new Date().getTime(),
        name: "貂蝉",
      });
      this.setState({
          arr:[...arr],//子组件接收arr,子组件会更新
        	//arr 这种方式,子组件是不会触发渲染
      })
  }

2.如果prop和state每次都会变,那么PureComponent的效率还不如Component,进行浅比较也是需要时间;所以PureComponent 一般用在展示型组件上。

4.React.memo()

React.memo ,这个 API 可以说是对标类组件里面的 PureComponent ,这是可以减少重新 render 的次数的。

import React from 'react'

function Fn(props) {
    console.log("Memo 执行了");
    let {num,arr}=props;

    return (
        <div className="box">
            <h3>meno</h3>
            <div>num:{num}</div>
            <div>arr:{JSON.stringify(arr)}</div>
        </div>
    )
}
export default React.memo(Fn)

[注意]:React.memo()也是进行浅比较,对于对象和数组,同样的父组件需要使用拷贝才能引起其重新渲染。

5.Fragment

在React中,用React.Fragment组件能够在不额外创建DOM元素的情况下,让render()方法返回多个元素。你也可以使用<></>代替。

import React, { Component, Fragment } from "react";

class Item extends Component {
  render() {
    return (
      <Fragment>
        <li>测试1</li>
        <li>测试2</li>
      </Fragment>
    );
  }
}
export default class Mine extends Component {
  render() {
    return (
      <div>
        <h1>this is Mine </h1>
        <ul>
          <Item></Item>
        </ul>
      </div>
    );
  }
}
6.context

context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性.

context api给出三个概念:React.createContext()、Provider、Consumer.

1.语法介绍

React.createContext()

这个方法用来创建context对象,并包含Provider、Consumer两个组件

const {Provider, Consumer} = React.createContext();

Provider

数据的生产者,通过value属性接收存储的公共状态,来传递给子组件或后代组件

<Provider value={/* some value */}>

Consumer

数据的消费者,通过订阅Provider传入的context的值,来实时更新当前组件的状态

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>
2.举例

父组件:

// 1.创建一个Provider,Consumer对象
export let {Provider,Consumer}=React.createContext()
<div className="box">
  <h3>parent</h3>
  {/* 2.通过Provider提供数据,数据在value */}
  <Provider value={ {num:10,age:20} }>
    <Child></Child>
  </Provider>

</div>

子组件的子组件中

// 3.引入Consumer 
import {Consumer} from "./Parent"
<div className="box">
  <h3>child child</h3>

  {/* 4.使用数据 */}
  <Consumer>
    {
      (d)=>{
        return <div>{JSON.stringify(d)}</div>
      }
    }
  </Consumer>
</div>
7.错误边界处理

平时,我们在程序运行中,如果一旦报错,那么所有的程序都将暂停,并且所有的组件都不会出现在页面。如果项目上线,我们希望的实际如果某一个组件出现错误,其他组件正常运行,那么此时,我们需要使用componentDidCatch来实现。

封装ErrorBoundary组件

import React, { Component } from 'react';

class ErrorBoundary extends Component {
    state={
        hasError:false,
        error:null
    }
    componentDidCatch(error){
        this.setState({
            hasError:true,
            error
        })
    }
    render() {
        const {hasError,error}=this.state;
        return (
            <>
            {
                hasError?<div>很遗憾,此处加载错误</div>:this.props.children
            }
            </>
        );
    }
}

export default ErrorBoundary;

使用:

{/*错误边界处理*/}
<ErrorBoundary>
  <Error></Error>
</ErrorBoundary>
8.HOC高阶组件

HOC(Higher-order component)是一种React 的进阶使用方法,只要还是为了便于组件的复用。强调一点,HOC本身并不是 React API, 它就是一个方法,一个接收一个组件作为参数,返回一个增强的组件的方法。

1.语法

**
 * 1.高阶组件是一个函数
 * 2.参数是组件
 * 3.返回值也是组件
 */
const Fn = (C) => {
  return class extends Component {
    render() {
      return (
        <div className="box">
          <h3>此处是一个高阶组件</h3>
          <C {...this.props}></C>
        </div>
      );
    }
  };
};

//2个组件
class Com1 extends Component {
  render() {
    return <div>组件1</div>;
  }
}
class Com2 extends Component {
  render() {
    return <div>组件2</div>;
  }
}

//调用
const FnCom1 = Fn(Com1);
const FnCom2 = Fn(Com2);

2.案例

withRequest

import React, { Component } from 'react';
import axios from 'axios';
export default url => C => {
    return class H extends Component {
        constructor() {
            super()
            this.state = {
                arr: null
            }
        }
        componentDidMount() {
            axios.get(url).then(res => {
                this.setState({
                    arr: res.data.list
                })
            })
        }
        render() {
            let { arr } = this.state;
            return (
                <div>
                    {arr ? <C arr={arr}></C> : <div>正在加载中。。。</div>}
                </div>
            );
        }
    }
}
原文地址:https://www.cnblogs.com/chenhaiyun/p/14869060.html