React基础知识总结

一:组件组合

1.1 含义

组件当中也是可以嵌套其它元素或者组件的。

1.2 实现方式:

1.在父组件中嵌套其他组件 正常写内容

2.在子组件中通过 props.children 获取组件内部嵌套的内容

1.3 代码实现

父组件:Compose.jsx:

//父组Compose.jsx件要嵌套Child.jsx组件
import React, { Component } from 'react'
import Child from './Child'

export default class Compose extends Component {
  render() {
    const num = 100
    return (
      <div>
        <Child>
            <h3>我是标题</h3>
            <div>
                <p>我是文本{ num }</p>
            </div>
        </Child>
      </div>
    )
  }
}

子组件 通过 props.children 获取组件内部嵌套的内容

子组件:Child.jsx:

import React, { Component } from 'react'

export default class Child extends Component {
  render() {
    return (
      <div>
        Child
        <h3>{ this.props.children }</h3>
      </div>
    )
  }
}

二:组件状态管理

2.1 含义

State 与 props 类似,但是 state 是组件私有的,并且完全受控于所属的组件。

使用 State的目的 :管理组件内部状态,并且当组件内部状态发生变化的时候,重新渲染页面

2.2 定义数据的方式

2.2.1 constructor里定义数据

特点:

(1)数据是响应式(当数据改变,视图跟着改变)

(2)使用: this.state.变量名

2.2.2 直接定义数据

特点:

(1)定义的数据不是响应式

(2)使用:this.变量名

2.2.3 区别:

定义在constructor中的数据 ,通过指定的方法(this.setState({})修改state数据 ,同步视图

直接定义的数据,修改state数据,视图不变     this.setState({ })

//引入类组件的快捷键:rcc
import React, { Component } from 'react'

export default class Data extends Component {
    constructor() {
        super();
        //第一种在constructor中定义状态
        this.state = {
            msg: 'hi,React',
            count: 10,
            obj: {
                name: '张艺兴',
                age: 30
            }
        }
    }

    // 第二种直接定义数据
    num = 100;

    render() {
        return (
            <div>
                <h2>React 状态</h2>
                {/* 使用constructor中定义的数据 */}
                <p>msg:{this.state.msg}</p>
                {/* 使用直接定义的数据 */}
                <p>num:{this.num}</p>
            </div>
        )
    }
}

2.3 修改state数据的方式

小程序如何修改状态

setData({ ..... })

 React中:修改state数据需要使用 setState({ })

this.setState({
    count:....
})

2.3.1 setState(obj, callback)基本用法

  • setState 的第一个参数: 当 obj 为一个对象的时候,则为即将合并的 state,如何 obj 是一个函数,那么当前组件的 state 和 props 将作为参数,返回值用于合并新的 state。
  • setState 的第二个参数:当 callback 为一个函数,函数执行上下文中可以获取当前 setState 更新后的最新 state 的值,可以作为以来 state 变化的副作用函数,可以 用来做一些基本的 DOM 操作等。
  • 调用setState,组件的render方法会被重新调用,从而重新渲染页面
  • 调用setState,子组件也会被重新渲染

2.3.2 用法示例

// 第一个参数为function类型的时候
 
this.setState((state, props) => {
    return { number: 1 }
})
 
// 第一个参数为object类型的时候
this.setState(
    { number: 1 }, 
    () => {
    console.log(this.state.number) // 获取最新的number
    }
)

【说明】 事件中this指向当前组件的方式

  1. 普通函数: this是undefined
  2. 箭头函数: this指向当前的组件实例对象,可以访问组件中数据 和方法

那this指向了undefined

那如何从视图中的this传到事件中? 

解决方法一:使用 bind 指向当前组件,让普通函数有 this
  ...
      clickHandler(){
        // 改变状态
        this.setState({
            num:100
        })
    }
    ...
    render(){
           return(
               <div>
                   //使用bind指向当前组件,让普通函数有this
                    <button onClick={ this.clickHandler.bind(this) }>改变状态</button>
               </div>           
           ) 
    }
...
解决方法二: 绑定 this
export default class StateDemo extends Component {
    constructor(){
        super()
        // 定义状态
        this.state = {
            num:10
        } 
        // 绑定this
        this.clickHandler2 = this.clickHandler2.bind(this)
    }
    
    clickHandler2(){
        this.setState({
            num:1000
        })
    }
    
    render() {
        return (
            <div>
                {/* 读取状态 */}
                <p>{ this.state.num }</p>
                <button onClick={ this.clickHandler2 }>改变状态2</button>               
            </div>
        )
    }
}
解决方法三:使用箭头函数
import React, { Component } from 'react'
export default class StateDemo extends Component {
    constructor(){
        super()
        // 定义状态
        this.state = {
            num:10
        } 
    }
    clickHandler3 = () =>{
        this.setState({
            num:10000
        })
    }

    render() {
        return (
            <div>
                <h3>State状态</h3>
                {/* 读取状态 */}
                <p>{ this.state.num }</p>
                <button onClick={ this.clickHandler3 }>改变状态3</button>
            </div>
        )
    }
}

2.4 State 与 Props 的区别

State:负责组件内部的状态管理

Props:负责组件与组件之间的数据交互

注意:

1.props传递的数据可以是state数据

import React, { Component } from 'react'
import SAPChild from './SAPChild'
import SAPChild2 from './SAPChild2'

export default class StateAndProps extends Component {
  constructor(){
    super()
    this.state = {
      title:"我是状态标题"
    }
  }
  clickHandler = () =>{
    this.setState({
      title:"我是改变了的标题"
    })
  }

  render() {
    return (
      <div>
        <h3>StateAndProps</h3>
        <p>{ this.state.title }</p>
        <SAPChild title={ this.state.title }/>
        <SAPChild title="哈哈哈"/>
        <SAPChild2 />
        //props传递的数据可以是state数据
        <button onClick={ this.clickHandler }>修改状态</button>
      </div>
    )
  }
}

2.setState重新渲染组件和子组件

每一次父组件的状态发生改变,并且没有做特殊的处理,无论子组件有无变化,(子组件)都会重新渲染

3.基于props初始化state

若希望传过来的属性作为子组件的状态值,需要依赖构造函数中的super ,且需要传递props进来

如下:

PropsAndState3父组件

import React, { Component } from 'react';
import PropsAndState4 from './PropsAndState4';

export default class PropsAndState3 extends Component {
  render() {
    return (
      <div>
        <h3>PropsAndState3</h3>
        <PropsAndState4 title={'我是标题'} />
      </div>
    );
  }
}

PropsAndState4 子组件

import React, { Component } from 'react';
export default class PropsAndState4 extends Component {
  constructor(props) {
    super(props);
    this.state = {
      title: props.title,
    };
  }
  render() {
    return (
      <div>
        <h3>PropsAndState4</h3>
        <p>{this.state.title}</p>
      </div>
    );
  }
}

三:生命周期函数

生命周期图例:React lifecycle methods diagram

3.1 生命周期函数

生命周期函数(7个,废掉了3个还剩4个)

  1. componentWillMount :组件将要被渲染,还没渲染,没东西(17废掉,但可以使用)
  2. componentDidMount : 组件渲染完成(组件进入运行状态) 【重点:网络请求,绑定事件】
  3. shouldComponentUpdate: 是否允许组件更新(这个函数中必须有返回值,如果返回true,表示允许更新;如果返回false,表示不允许更新;默认允许 ) 【问询函数】
  4. componentWillUpdate : 组件将要开始更新(17废掉,但可以使用)
  5. componentDidUpdate : 组件更新完成(重新进入运行状态)
  6. componentWillReceiveProps: 组件接收props更新(17废掉,但可以使用)
  7. componentWillUnmount: 组件将要卸载 ,还可以访问this, this.setState()只执行一次 【重点:收尾工作, 清空定时器】

3.2 组件的挂载

组件在首次创建后,进行第一次的渲染为挂载期。挂载期有的一些方法会被依次触发,列举如下:

  • constructor :构造函数,组件被初始化时调用,在生命周期中只会被调用一次。
  • static getDerivedStateFromProps :是组件类的静态函数, 组件 props 或者 state 初始化或者变化时调用,最终可以返回一个新的state对象,使用率低。
  • render :定义并返回组件的界面(react元素)。
  • componentDidMount : 生成了真实DOM并被渲染到页面之后调用,在生命周期中只会被调用一次

3.3 组件的更新

组件更新,指的是在组件初次渲染后,进行了组件状态的改变。React在生命周期中的更新过程包括以下几个方法:

  • static getDerivedStateFromProps :组件 props 或者 state 变化时调用
  • shouldComponentUpdate : 返回布尔值,true则组件会重新渲染,false则不会重新渲染。
  • render :定义并返回组件的界面(react元素)。
  • getSnapshotBeforeUpdate :在更新DOM之前调用,必须要返回一个值,要与
  • componentDidUpdate() 方法一起使用。
  • componentDidUpdate : 页面更新之后调用

3.4 组件的卸载

生命周期的最后一个过程为组件卸载期,也称为组件销毁期。该过程主要涉及一个 方法

  • componentWillUnmount :组件卸载之前调用。我们可以在这个钩子函数里面执行必要的清理操作。

例如,清除定时器,关闭持续的网络连接,移除监听器等。

四:表单

4.1 受控组件

思考:vue如何双向数据绑定?

....

当页面有多个输入框的时候,为每个输入框都添加一个独立的事件处理函数太过麻烦

我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作

受到状态管理控制的组件称作受控组件

import React, { Component } from 'react';

export default class FromDemo01 extends Component {
  constructor() {
    super();
    this.state = {
      username: '',
    };
  }
  onChangeHandler = (e) => {
    this.setState({
      username: e.target.value,
    });
    // console.log(e.target.value);
  };
  render() {
    return (
      <div>
        <h3>受控组件</h3>
        <form>
          {/* 通过value的方式绑定username -通过状态管理*/}
          <input
            type="text"
            value={this.state.username}
            onChange={this.onChangeHandler}
          />
          <p>{this.state.username}</p>
        </form>
      </div>
    );
  }
}

 4.2 受控组件处理多个输入

import React, { Component } from 'react';

export default class FormDemo03 extends Component {
  constructor() {
    super();
    this.state = {
      username: '',
      password: '',
    };
  }
  onChangeHandler = (e) => {
    this.setState({
      //Es6对象的扩展:ES6 允许字面量定义对象时,用表达式法作为对象的属性名,即把表达式放在方括号内。
      // 实现动态的key
      [e.target.name]: e.target.value,
    });
  };

  loginHandler = (e) => {
    //阻止默认跳转事件
    e.preventDefault();
    console.log(this.state);
  };
  render() {
    return (
      <div>
        <h3>受控组件处理多个输入</h3>
        <form action="">
          <input
            type="text"
            name="username"
            value={this.state.username}
            onChange={this.onChangeHandler}
          />
          <br />
          <input
            type="password"
            name="password"
            value={this.state.password}
            onChange={this.onChangeHandler}
          />
          {/* <input type="text" value={this.state.password} /> */}
          <button onClick={this.loginHandler}>登录</button>
        </form>
      </div>
    );
  }
}

 4.3 非受控组件

在一个受控组件中,表单数据是由 React 组件来管理的,为每一个受控组件添加事件处理函数,这并不完美。

可以使用替代方案:使用非受控组件,这时表单数据将由 DOM 节点来处理,可以使用ref来从DOM节点中获取表单数据

ref功能:

① 获取DOM元素

② 获取子组件实例

4.3.1. 创建步骤

① 在构造函数中创建ref语法:this.myRef = React.createRef()

② 给元素绑定ref属性

③ 获DOM元素:this.myRef.current

4.3.2.使用
import React, { Component } from 'react';

export default class FormDemo02 extends Component {
  constructor() {
    super();
    //1.在构造函数中创建Ref语法
    this.username = React.createRef();
  }
  loginHandler = (e) => {
    e.preventDefault();
    //3.获取子组件
    console.log(this.username.current.value);
  };
  render() {
    return (
      <div>
        <h3>非受控组件</h3>
        <form action="">
        {/* 2.给子组件挂载ref属性 */}
          <input type="text" ref={this.username} />
          <button onClick={this.loginHandler}>登录</button>
        </form>
      </div>
    );
  }
}

五:Context跨组件传递数据

5.1介绍

跨层级传值,数据深层传递

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

在一个典型的React应用中,数据是通过props属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是及其繁琐的

(例如:地区偏好,UI主题),这些属性是应用程序中许多组件都需要的。

Context提供了一种在组件之间共享此类值的方式,而不必通过组件树的逐层传递props。

5.2 使用规范

5.2.1 创建一个context对象

//MyContext.js
// 引入react
import React from "react";

export default React.createContext({ message: '默认值', age: 20 })

5.2.2 提供数据

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

即Provider属性,整体作为标签使用,这个标签上提供的一个属性value,作用是提供数据

ComponentA.jsx组件

//导入
import MyContext from './MyContext.js';

<MyContext.Provider  value={{ message: this.state.message, age: this.state.age }} >
   {/*被包含的组件以及包含组件的后代都可以使用value数据*/}
   {/*子组件*/}
    <ComponentB />
</MyContext.Provider>

5.2.3 使用数据

如果我们要在组件树中去共享某些数据,并且要避免通过中间元素传递 props,则可以使用Context来实现

方式一:子组件名称.contextType 属性形式

设置类的contentType属性,从而获取context对象

ComponentC.jsx组件:

//子组件名称.contextType = Mycontext;
//组件内使用:
//ComponentC.jsx
import React, { Component } from 'react'
import MyContext from './MyContext'实时效果反馈

export default class ComponentC extends Component {
    constructor() {
        super();
        this.state = {
          message: '我是a的信息',
          age: 20,
        };
     }
    render() {
        return (
          <div>
            <h3>ComponentC</h3>
            {/* 获取A里的value */}
            <p>{this.context.message}</p> //我是a的信息
            <p>{this.context.age}</p> //20
          </div>
        );
      }
    }
    //设置类的contentType属性,从而获取context对象
ComponentC.contextType = MyContext;

方式二:Context.Consumer使用数据 标签方式

作用:使用Provider上提供的数据

使用:也是作为标签使用

语法:

<MyContext.Consumer>
    {value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>  

ComponentD.jsx: 

import React from 'react';
import myContext from '../utils/myContext';

export default function ComponentD() {
  console.log(this); //undefined
  return (
    <div>
      <h3>componentD</h3>
      {/* 在函数式组件中获取context数据 */}
      <myContext.Consumer>
        {
            value => (
              <div>
                <p>消息:{value.message}</p>
                <p>年龄:{value.age}</p>
              </div>
            )
        }
      </myContext.Consumer>
    </div>
  );
}

预览

注意:
  • 每个组件实例本身就有context属性,默认为空
  • 需要当前子组件引入,并赋值

Class.contextType 和 Context.Consumer 的区别

  1. Class.contextType使用时会自动在组件上添加一个属性 context,存储离当前组件最近的Provider提供的数据,不能写多个
  2. Context.Consumer 可以写多个,能获取到所有Provider提供数据

5.3 Context更新

Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。

提示:

消费组件是否重新渲染不受shouldComponentUpdate的控制。

其父组件使用shouldComponentUpdate停止了渲染或者消费组件本身使用

shouldComponentUpdate停止渲染,也不影响消费组件的继续更新。

在ComponenC.jsx:中设置一个shouldComponentUpdate可以发现 并不不能影响消费组件ComponentD.jsx的继续更新

import React, { Component } from 'react';
import MyContext from '../utils/myContext';

export default class ComponentC extends Component {
  constructor() {
    super();
    this.state = {
      num: 10,
    };
  }
  componentDidMount() {
    this.setState({
      num: 1000,
    });
  }
  // 阻止生自动渲染-阻止不了  说明context可以强制ui刷新,优先级很强
  shouldComponentUpdate() {
    return false;
  }

  render() {
    return (
      <div>
        <h3>ComponentC</h3>
        {/* A里的value */}
        <p>{this.context.message}</p>
        <p>{this.context.age}</p>
        <p>{this.state.num}</p>
      </div>
    );
  }
}

// 通过设置contextType属性 来获取context对象
ComponentC.contextType = MyContext;

ComponentD.jsx:

import React from 'react';
import myContext from '../utils/myContext';

export default function ComponentD() {
  console.log(this); //undefined
  return (
    <div>
      <h3>componentD</h3>
      {/* 在函数式组件中获取context数据 */}
      <myContext.Consumer>
        {(value) => (
          <div>
            <p>消息:{value.message}</p>
            <p>年龄:{value.age}</p>
          </div>
        )}
      </myContext.Consumer>
    </div>
  );
}

预览:

点击前:

点击后:

六:Fragments

七:错误边界

八:Refs & DOM

8.1 获取dom的引用

8.1.2 使用步骤

(1)创建 Refs

使用 React.createRef() 创建的。

通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。

在构造函数中创建ref语法:this.myRef = React.createRef()

(2)绑定Refs

给对应的React 元素设置 ref 属性,则相当于使用 ref 去存储 DOM 节点的引用。

{ /* 给元素绑定ref属性 */ }

(3)访问Refs

当 ref 被传递给 render 中的元素时,对该节点的引用可以在 ref 的 current 属性中被访问

操作dom 访问ref的current属性 this.自定义ref事件.current.要操作的属性

8.1.3 代码实现:

(1)创建 Refs

constructor(){
    super()
    //this.自定义ref =  React.createRef();
    this.changeHello =  React.createRef();
}

(2)使用Refs

render(){
    return(
        <div>
           {/* 把hello变成helloword  直接操作dom元素 */}
           {/* 使用ref 三步骤*/}
           {/* 给元素绑定ref属性 <E ref={this.myRef}></E> */}
            <p ref={ this.changeHello }>hello</p>
        </div>    
    )
}

(3)访问Refs,操作dom

import React, { Component } from 'react';

export default class Refs01 extends Component {
  constructor() {
    super();
    //   1.在构造函数中创建ref语法:this.myRef = React.createRef()
    this.changeHello = React.createRef();
  }
  changeDom = () => {
    //  3.获取子组件并操作dom
    console.log('dom元素是:', this.changeHello.current.innerHTML);
    this.changeHello.current.innerHTML = '已修改';
  };
  render() {
    return (
      <div>
        <h1>refs获取子元素</h1>
        {/* 2.给元素绑定ref属性 <E ref={this.myRef}></E> */}
        <p ref={this.changeHello}>hello</p>
        {/* 把hello变成helloword  直接操作dom元素 */}
        {/* 使用ref 三步骤*/}
        <button onClick={this.changeDom}>获DOM元素并修改</button>
      </div>
    );
  }
}

提示:

React 会在组件挂载时给 current 属性传入 DOM 元素,并在组件卸载时传入 null 值。

ref会在 componentDidMount 或 componentDidUpdate 生命周期钩子触发前更新

8.2 获取组件类实例

使用Refs的时候,当 ref 属性用于自定义 class 组件时, ref 对象接收组件的实例作为其 current 属性。

8.2.1步骤和实现

① 在子组件中定义方法和属性:
//在子组件中定义方法和属性:
import React, { Component } from 'react';

export default class RefsCom02 extends Component {
  constructor() {
    super();
    this.state = {
      num: 100,
    };
  }
  //在子组件中定义方法
  getInfo() {
    console.log('我是子组件的信息内容');
  }
  render() {
    return (
      <div>
        <h2>子组件内定义组件的实例方法和属性</h2>
            {/* 定义属性  */}
        <p>子组件的属性:{this.state.num}</p>
      </div>
    );
  }
}
② 在父组件中使用refs来强行使用子组件的方法和属性
import React, { Component } from 'react';
import RefsCom02 from './RefsCom02';

export default class Refs01 extends Component {
  constructor() {
    super();
    //   在构造函数中创建ref语法:this.myRef = React.createRef()
    this.getChild = React.createRef();
  }
  
  // 在生命周期函数中读取子组件的实例方法和属性
  componentDidMount() {
    this.getChild.current.getInfo(); //使用子组件的方法  --我是子组件的信息内容
    console.log(this.getChild.current.state.num); //查看子组件的属性 --100
  }

  render() {
    return (
      <div>
          <h2>父组件:refs获取组件类实例方法和属性</h2>
          <RefsCom02 ref={this.getChild} />
      </div>
    );
  }
}

8.3 转发 refs 到 DOM 组件

8.4 回调 Refs

九:高阶组件

9.1 含义

高阶组件(HOC:Higher-Order Components)是一个函数

高阶组件(HOC) 接受一个组件为参数,返回值为新组件

可以把 高阶组件 看作是 组件的加工厂 ,接收旧组件返回包装后的新组件

9.2 什么情况下使用高阶组件

react 如果有多个组件都用到了 同一段逻辑 , 这时,就可以把共同的逻辑部分提取出来,利用高阶组件的形式

将这段逻辑整合到每一个组件中, 从而减少代码的逻辑重复

9.3 案例

在本地存储中存储一个字段和对应的值

 存储成功后:

假设一个使用高阶组件的场景:多个组件都要读到这个key和value

普通的写法是每一个组件都需要写相同读取数据的逻辑

import React, { Component } from 'react';

export default class HOCdemo1 extends Component {
  constructor() {
    super();
    this.state = {
      info: '',
    };
  }
  componentDidMount() {
    this.setState({
        //把设置的value值赋值给info
      info: localStorage.getItem('key'),
    });
  }
  render() {
    return (
      <div>
        <h3>HOCdemo01</h3>
        <p>{this.state.info}</p>  {/*hello*/}
      </div>
    );
  }
}

把相同的逻辑提取出来作为高阶组件

SameInfo.jsx:

import React from 'react';

// sameInfo  函数名
//ComponentInfo  旧组件
// return 新组件
export default function sameInfo(ComponentInfo) {
  return class extends React.Component {
    //   提取共同的逻辑
    constructor() {
      super();
      this.state = {
        info: '',
      };
    }
    componentDidMount() {
      this.setState({
        info: localStorage.getItem('key'),
      });
    }

    //   渲染成一个新的组件
    render() {
      // 返回一个旧组件
      // 组件的最终目的是读到info  因此info作为参数返回
      // ...this.props 传递给当前组件的属性继续向下传递
      return <ComponentInfo info={this.state.info} {...this.props} />;
    }
  };
}

使用高阶组件:-创建新组建-使用相同的逻辑

UseDemo01.jsx:

import React, { Component } from 'react';
// 引入高阶组件
import sameInfo from './SameInfo';

class UseDemo01 extends Component {
  render() {
    return (
      <div>
        <h2>使用高阶组件1</h2>
        <p>{this.props.info}</p>
      </div>
    );
  }
}
export default sameInfo(UseDemo01);

 预览:

9.4 高阶组件(HOC)组合参数组件

我们来思考一个问题,使用高阶组件是否可以修改一个不满足当前效果的组件呢?

答:通过组件的原型,重绘了整个render函数

高阶组件withHigh.jsx:

import React from 'react';

export default function withHigh(ComponentNum) {
  // 修改传入的组件的render函数
  ComponentNum.prototype.render = function () {
    return <div>使用高阶组件修改后的界面</div>;
  };

  //返回修改后的组件
  return ComponentNum;
}

 预览:

原先:

 使用高阶组件强行更改后的界面:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值