react训练通关之基础知识点目录

134 阅读10分钟

react基础

先别急着看技术,看看美图摸鱼一手🤔。 image.png

1. 前言

  • 随着前端世界的蓬勃发展,伟大的人类创造了react(好好好,你小子vue一句不提),随着react,vue,angular的出现,使得前端世界进入三国分裂时期。当然在国内只有vue与react,angular玩的人很少。
  • 什么是react? react.js是用于构建用户界面的 一个js 库,相较于vue2而言,更为贴近原生事件。
  • react有什么用?React 提供了组件式的开发、声明式的界面和响应式 DOM 更改(虚拟dom),这让我们可以😌更简洁的出完美的项目。

2. 类组件与函数组件的基本结构

  1. 类组件
    1.须继承React.Component
    2.必须实现render()方法
    3.构造函数constructor()可选:如果不写constructor,会使用默认构造函数
    4.使用箭头函数不需要将函数bind在constructor下
    5.super() 和 super(props),如果super() 中不传入props,this.props会输出undefined

注意:super关键字,它指代父类的实例(即指代父类的this对象),子类在没有自己的this对象情况下,去继承父类的this对象使用。子类需要在constructor方法中调用super方法,从而得到父类的this对象,否则会报错。简单来说,就是需要使用this,就得加上super🤪

// 默认构造函数
constructor(props, context) {
    super(props, context);
   }
   
// 类组件基本结构
import React from "react";

class ClassComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "React",
      number: 0,
    };
    this.noBind = this.noBind.bind(this)
  }
  componentDidMount() {}
  
  handleClick = () => {  // 使用箭头函数不需要将函数bind在constructor下
    this.setState((state, props) => {
      return { number: this.state.number+1 };
    });
  };
  noBind(){
    this.setState((state, props) => {
      return { number: this.state.number+1 };
    });
  }

  render() {
    return (
      <div>
        {this.state.name}: {this.state.number}
        <button onClick={this.handleClick}>number++</button>
        <button onClick={this.noBind}>number++</button>
      </div>
    );
  }
}

export default ClassComponent;
  1. 函数组件
    1.与类组件不同,函数组件不需要继承React.Component
    2.使用Hooks管理状态,如useState
    3.函数组件使用useEffect Hook来模拟生命周期方法
    4.函数组件可以接收props作为参数
import React,{ useState } from "react";

export default function Func(params) {
// const [state, setState] = useState("state");
  const {state,changeState} = params
  
  useEffect(() => {
    // 相当于 componentDidMount 和 componentDidUpdate
    return () => {
      // 清理函数,相当于 componentWillUnmount
    };
  }, []);

  const changeState = () => {
    setState("change");
  };
  
  return (
    <div>
      <div>{state}</div>
      <button onClick={changeState}>changeState</button>
    </div>
  );
}

总结:
1. 定义方式:类组件使用class关键字,函数组件是普通函数。
2. this关键字:类组件可以使用this,函数组件没有this。
3. 状态管理:类组件使用this.state和this.setState(),函数组件使用useState Hook,react训练通关之常用hook
4. 生命周期:类组件有完整的生命周期方法,函数组件使用useEffect Hook模拟生命周期。
5. props访问:类组件通过this.props访问,函数组件直接通过参数访问。

3. 状态(State)

1.类组件中的setState

   setState(obj,callback)
   1. obj:当obj为一个对象,则为即将合并的 state ;
         obj 是一个函数,那么当前组件的 state 和 props 将作为参数,返回值用于合并新的 state。
   2. callback:callback 为一个函数,函数执行上下文中可以获取当前 setState 更新后的最新 state 的值,可以作为依赖 state 变化的副作用函数,可以用来做一些基于 DOM 的操作。
   3. setState()有同步有异步,基本上都是异步更新,自己定义的DOM事件里setState()是同步的

2.函数组件中的useState

 [ state , dispatch ] = useState(initData)
     1. state,视图渲染的数据。
     2. dispatch作为改变state的回调,用于对state值的更新。
     3. initData 有两种情况,
        第一种情况是非函数,作为state的初始值。 
        第二种情况是函数,将函数返回后的值作为state的初始值。

这是一道常见的面试题下🤣:state 到底是同步还是异步的?

一般的思路都是围绕batchUpdate 批量更新概念展开的,答出了批量更新被打破的条件,以及概念基本就可以解决这道面试题。

  1. 当我们进行setState时,其实对于react而言,就是调用了内部的 enqueueSetState方法进行update事件的创建,然后将事件放入当前 fiber 对象的待更新队列中,最后进行更新。
  2. react内部通过调用batchedEventUpdates识别是否开启批量更新,通过控制isBatchingEventUpdates的状态进行更新。

简单来说

  1. 对于开启isBatchingEventUpdates=true的事件,表现为同步更新
    如:生命周期、React 中注册的事件

  2. 对于不开启的事件(不是在 React 中注册的事件):表现为异步更新
    setTimeout/setInterval等、自定义 DOM 事件等
    说明,setTimeout执行时,会先将函数推入任务队列,此时批量更新会开启,但setTimeout结束时,会将批量更新关闭,然后才执行推入队列中的任务,此时表现为异步

// 异步更新
changeMessage() {
  this.setState({
    message: "NewMessage"
  })
  console.log(this.state.message); // message
}

// 同步更新,
changeMessage() {
  setTimeout(() => {
    this.setState({
      message: "NewMessage"
    });
    console.log(this.state.message); // NewMessage
  }, 0);
}
// flushSync 提升更新优先级,flushSync会合并之前的setState
ReactDOM.flushSync(()=>{ this.setState({ number: 3 }) })

4. 属性(Props)

React 的核心思想就是组件化思想,页面会被切分成一些独立的、可复用的组件。
Props作为参数传递的值,承接起了react组件化的数据传递任务。
porps作为输入,主要有以下几种传递类型

  1. props 作为渲染数据源传递。
  2. props 作为回调函数传递。
  3. props 作为组件传递。
  4. props 作为渲染函数传递。
  5. props 作为渲染函数使用 children 传递。
  6. props 作为组件使用 children 传递。
function ChidrenComponent(){
  return <div> this is ChidrenComponent </div>
}
class Index extends React.Component{
  state={  
      message: "React"
  }
  handleClick = () =>  this.setState({ message:'Hello React' })
  render(){
      return <div>
          <Children  
             message={this.state.message}              // 数据传递
             handleClick={ this.handleClick  }     // 回调函数传递
             Component={ ChidrenComponent }        // 组件传递
             renderName={ ()=><div> this is React </div> }  // 作为函数传递
          >
              { ()=> <div>this is renderReact</div>  } --->children[0]
              <ChidrenComponent />                     --->children[1]
          </Children>
      </div>
  }
}

// 子组件中通过以下方式
const { children, message, renderName, handleClick, Component } = this.props;

注意,props的抽离,与props的注入

// props的抽离
props = { meaasge: "react", number: 2,};
组件中使用 const { number, ...fatherProps  } = props 将number抽离
return <Son {...fatherProps} />; 子组件中获取到props为{ meaasge: "react"}

// 隐式注入
使用React.cloneElement(prop.children,{ meaasge:'this is React !' })进行props.chidren克隆与混入新的 props

5. 生命周期方法

  1. react16.3以前的生命周期:
    主要分为 初始化阶段更新阶段卸载组件

     1.初始化阶段
     constructor->componentWillMount->render->componentDidMount
     
     2.更新阶段
     componentWillReceiveProps->shouldComponentUpdate->componentWillUpdate->
     render->componentDidUpdate
     
     3.卸载组件
     componentWillUnmount
    
  2. react16.4及以后三个阶段主要有以下改变

     1.创建阶段
     constructor---->getDerivedStateFromProps---->render---->componentDidMount
    
     2.更新阶段
     getDerivedStateFromProps---->sholdComponentUpdate--->render---->getSnapshotBeforeUpdate
     --->componentDidupdate
    
     3.阶段
     componentWillUnmount
    

3.新旧生命周期对比

  • 生命周期详细介绍react 训练通关生命周期

  • 旧版生命周期: 16.3之前的版本包含以下生命周期方法:

    • componentWillMount()
    • componentWillReceiveProps(nextProps)
    • componentWillUpdate(nextProps, nextState)
  • 新版生命周期: 从16.3开始,React废弃了上述三个生命周期方法,并引入了以下新方法:

    • componentWillMount()去除
    • getDerivedStateFromProps(nextProps, prevState): 用于在渲染之前根据新的props更新state。
    • getSnapshotBeforeUpdate(prevProps, prevState): 在更新DOM之前调用,可以捕获一些信息(如滚动位置)

废弃上诉三个周期主要原因是避免误用: 旧的生命周期方法(如componentWillMountcomponentWillReceiveProps)经常被误解和滥用,导致不必要的副作用。

6. Refs和DOM

Refs 在计算机中称为弹性文件系统(英语:Resilient File System,简称ReFS)

  • React 中的 Refs主要有以下作用
    • 使用React.createRef()创建refs
    • 获取页面上的DOM节点
class Index extends React.Component{
  constructor(props){
     super(props)
     this.currentDom = React.createRef(null)
  }
  componentDidMount(){
      console.log(this.currentDom)   // 输出当前dom,currentDom
  }
  render= () => <div ref={ this.currentDom }>currentDom</div>
}

类组件获取 Ref 主要有三种方式

  • 字符串, 使用this.refs.xxx 进行实例的获取
  • ref对象,使用时,使用createRef()创建ref,使用时,使用this.myRef.current获取
  • 函数, 函数会在 DOM 被挂载时进行回调,这个函数会传入一个 元素对象,使用时,直接获取即可
import React from "react";

class Children extends React.Component{  
  render=()=><div>React,Children</div>
}

// 传入字符串
export default class MyComponent extends React.Component {
  componentDidMount() {
    console.log(this.refs);// 输出两个dom{myref,currentRef}
  }
  render = () => (
    <div>
      <div ref="myref">myref</div>
      <Children ref="currentRef" />
    </div>
  );
}

// 传入对象
export default class Index extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
    this.myRefDom = React.createRef();
  }
  // myRef = React.createRef() 
  // myRefDom = React.createRef() 
  componentDidMount() {
    console.log(this.myRef.current); // myRef
    console.log(this.myRefDom.current); // myRefDom
  }
  render() {
    return (
      <div>
        <div ref={(node) => (this.myRef = node)}>myRef</div>
        <Children ref={(node) => (this.myRefDom = node)} />
      </div>
    );
  }
}
// 传入函数
export default class Index extends React.Component {
  constructor(props) {
    super(props);
    // this.myRef = React.createRef();
    // this.myRefDom = React.createRef();
  }
  myRef = null // 也可使用React.createRef()
  myRefDom = null
  componentDidMount() {
    console.log(this.myRef); // myRef
    console.log(this.myRefDom); // myRef
  }
  render() {
    return (
      <div>
        <div ref={ this.myRef }>myRef</div>
        <Children ref={this.myRefDom} />
      </div>
    );
  }
}

示例结果如下图 Image_20240724105007.png 函数组件使用 useRef 获取 Ref

const Index = (props) => {
  const myref = useRef()
  return (
    <>
      <div ref={myref}></div>
    </>
  )
}

7. 组件通信

  1. 父子组件传递传值:通过props
  2. 子组件向父组件传递数据:通过父组件的回调函数
  3. 兄弟组件之间通信:通过共同的父组件,进行数据的统一管理
import React, { useState } from "react";

const Children = (props) => {
  const { state, changeState } = props;
  return (
    <div>
      <div>{state}</div>
      <button onClick={() => changeState()}>changeState</button>
    </div>
  );
};

export default function father() {
  const [state, setState] = useState("state");
  const changeState = () => {
    setState("change");
  };
  return <Children changeState={changeState} state={state}></Children>;
}

  1. 跨多层级组件通信:使用Context API
    Context提供了一种在组件树中共享数据的方法,而不必显式地通过每个层级传递props。

  2. 使用Refs, 可以使用Refs直接访问子组件的实例,从而实现数据等传递与修改。

  3. 全局状态管理:使用Redux, MobX等状态管理库, 浏览器缓存localStorage等

  4. 使用react路由传参,进行页面间的数据传递修改
    React-dom 17版本以上 useHistory 更新为useNavigate传值

import { useNavigate } from "react-router-dom";

export default function myNavigateTo(){
  const navigateTo = useNavigate();

  const routerPush = () =>{
    navigateTo(`/userinfo?aa=121`,{state: {name:'nickName',sex: 'man',age:23}}) // state传参
    // navigateTo({pathname:'/userinfo',search:'?id=1'}   // search传参
  }
  return (
    <div>
      <button onClick={routerPush}></button>
    </div>
  )
}

import { useLocation } from "react-router-dom";

export default function userInfo(){
  const localtion = useLocation();

  console.log(localtion,'---------')
  // function useQuery() {  // search传参获取参数
  //   return queryString.parse(localtion.search);
  // }
  // const query = useQuery()
  // console.log(localtion.search) //{id: 1}
  return (
    <div>
    </div>
  )
}


react路由传参详细介绍

8. 高阶组件(HOC)

HOC 解决什么问题,什么时候用到 HOC,以及如何编写 HOC ?

  1. react的HOC的目的为了解决大量的代码复用,逻辑复用的问题。
  2. 高阶组件(Higher-order Component)是一个组件(本质上其实是一个函数),它接受一个组件作为参数,并返回一个新的组件。这个新的组件具有一些增强的特性或功能。简单来说,高阶组件就是用来“包装”其他组件,以提供额外的功能或逻辑
// 基础用法
const EnhancedComponent = highOrderComponent(WrappedComponent);

// 向新组件注入一个额外的 `extraProp` 属性
function withExtraProps(WrappedComponent) {
  return class extends React.Component {
    render() {
      return <WrappedComponent extraProp="This is an extra prop" {...this.props} />;
    }
  };
}

高阶组件的用途

  1. 复用逻辑:HOC 可以在多个组件之间共享相同的逻辑。例如,可以使用 HOC 来处理数据获取、状态管理、权限控制等。
  2. 条件渲染:HOC 可以根据某些条件决定是否渲染某个组件。
  3. 修改 Props:HOC 可以在传递给组件的 props 中添加、修改或删除属性。
  4. 增强组件:HOC 可以为组件添加额外的功能,例如错误边界、性能监控等
// 性能监控
function withPerformanceMonitor(WrappedComponent) {
  return class extends React.Component {
    componentDidMount() {
      this.startTime = performance.now();
    }

    componentDidUpdate() {
      const endTime = performance.now();
      console.log(`Render time: ${endTime - this.startTime}ms`);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
}

9. 受控组件与非受控组件

1.受控组件,其数据的流转主要受外部控制,
如以下代码中的username,input组件组件的状态全程响应state控制,
先从this.state进行获取value,再由onChange事件进行 this.setState的数据更新

class TestComponent extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      username: ""
    }
  }
  onChange (e) {
    console.log(e.target.value);
    this.setState({
      username: e.target.value
    })
  }
  render () {
    return <input name="username" value={this.state.username} onChange={(e) => this.onChange(e)} />
  }
}

2.非受控组件 非受控组件,就是不受我们控制的组件,他的state由他内部控制 一般情况是在初始化的时候接受外部数据,然后自己在内部存储其自身状态 当需要时,可以使用ref 查询 DOM并查找其当前值,如下:

import React, { Component } from 'react';

export class UnControll extends Component {
  constructor (props) {
    super(props);
    this.inputRef = React.createRef();
  }
  handleSubmit = (e) => {
    console.log('input的值为', this.inputRef.current.value);
    e.preventDefault();
  }
  render () {
    return (
      <form onSubmit={e => this.handleSubmit(e)}>
        <input defaultValue="lindaidai" ref={this.inputRef} />
        <input type="submit" value="提交" />
      </form>
    )
  }
}

10.Context

Context 通过组件树提供了一个传递数据的方法,解决了多重组件传值的问题。

  1. 使用Context的主要场景是:

    • 需要在多层组件中共享数据
    • 避免props逐层传递造成的繁琐
  2. 创建和使用Context的基本步骤:

    • 使用React.createContext()创建一个Context对象
    • 使用Context.Provider包裹父组件,并通过value属性提供要共享的数据
    • 在子组件中使用Context.Consumer来消费Context中的数据
const MyContext = React.createContext(defaultValue);

<MyContext.Provider value={/* some value */}>
  <ChildComponent />
</MyContext.Provider>

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

  • 注意:
    • Context的更新会触发使用该Context的组件重新渲染。
    • 可以嵌套多个Provider来覆盖Context的值。
    • 使用Context时需要注意性能问题,避免不必要的重渲染。