UzumakiItachi
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

UzumakiItachi

起风了,唯有努力生存。
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • React三大基础Hook使用
    • 前言
    • useState
    • useEffect
    • useContext
      • React.createContext
  • 关于严格模式下useEffect执行两次的问题
  • 从零搭建一个React管理系统
  • useEffect闭包陷阱
  • 《React18》
hanhanbuku
2023-03-14
目录

React三大基础Hook使用

# 前言

上一次写React还是class组件横行的年代,但是由于一直没有合适的项目做所以也丢的差不多了。这次正好从hook开始重学一遍React,从本文开始此React系列将是重学React的笔记,做一个记录。在16.8以后React退出了hooks的写法,可以让你在函数组件中使用类组件的state以及其他的react特性。也就是说全面拥抱函数式编程了,class组件已然没落了。下面就开始学习一下React官方提供的几个基础hooks吧

# useState

useState是React官方提供的一个可以在函数中使用state的hook。他和vue3的响应式api非常像,在响应式api出现前,data被绑定在了组件的配置项里,导致开发过程非常死板不灵活,而有了响应式api则可以随处定义响应式变量,代码复用逻辑复用变得更加灵活。useState也是起到相同的作用,在他出现前,函数式组件只能作为一个无状态组件,有状态的都必须用class组件去实现,有了他我们也可以随处抽离逻辑了。

useState的语法如下

const [state, setState] = useState(initialState);
1

useState函数接收一个默认值,返回一个数组,数组第一项是状态自身,第二项是修改这个值的函数,所以一般使用都是将这两项解构出来。

这个api比较简单就不做更多的演示了,直接看下一个

# useEffect

useEffect是一个非常有意思的hooks,他会根据传入的参数个数和传参类型不同起到不同的作用。

useEffect(() => {
    console.log('useEffect被执行')
});
1
2
3

上面是一个最简单的例子,而我们传入的回调函数会在每次dom更新之后被执行。可以这时候会觉得他和componentDidUpdate非常像了,其实不是的,useEffect会在浏览器完成布局与绘制之后,在一个延迟事件中被调用。 这使得他可以用来做很多副作用函数,而不会阻塞浏览器的渲染。

如果想要让useEffect只执行一次,并且销毁组件后还可以执行一次回调可以这样写

useEffect(() => {
    console.log('useEffect被执行')
    return ()=>{
        console.log('useEffect跟着组件一起销毁')
    }
});
1
2
3
4
5
6

这样useEffect会在组件渲染之后执行一次,并且在组件销毁前执行一次return的函数,这样做是为了防止内存泄露。

怎么样是不是很有意思,感觉他结合了class组件的好几个生命周期。

接下来看看传入两个参数有什么作用

useEffect(() => {
    console.log('useEffect被执行')
},[]);
1
2
3

上面例子说了如果只传入一个函数,那useEffect会被执行多次,而如果我们只想要他执行一次就可以传入第二个参数,一个空数组。至于为什么是空数组,看完下面的例子就明白了

下面介绍他最大的作用,监听

是的没错,就是和vue的watch一样

const [id,setId] = useState('')
useEffect(() => {
    console.log('useEffect被执行')
},[id]);
1
2
3
4

在第二个参数中传入一个非空数组,这样一来 只要id发生了变化useEffect就会被再次执行。就相当于监听id的变化一样。并且不只是传入一个id,[]里面可以传入多个参数,而只要这些参数里其中一个发生了变化都会触发 useEffect的再次执行。(注意:如果依赖的是一个引用类型,那react会以内存地址为依据去比对,也就是说只要内存地址没变,即使这个变量的属性变了也并不会触发useEffect的执行。所以想要监听引用类型的话最好是去监听他的某个属性)

下面在补充一下一种情况

useEffect(() => {
    console.log('useEffect被执行')
    return ()=>{
        console.log('useEffect跟着组件一起销毁')
    }
},[id]);
1
2
3
4
5
6

如果第一个参数有返回一个销毁函数的话,那每次依赖项发生变化都会销毁上一个useEffect并且重新创建一个useEffect再执行。

# useContext

组件通信一直都是组件化开发的一个必不可少的问题,如何解决复杂场景的组件通信也是每个coder必须掌握的技巧。在react里组件传值也可以直接通过props传递,和vue一样,但是组件的隔代通信就和vue有点不同了。 回顾一下在vue中,组件的隔代通信除了vuex之外还有Eventbus,provide inject,$attr等。那在react中当然也是有提供组件隔代传值的方法的,比如传递上下文

先看看react官方对context的描述

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。

个人觉得其实和eventbus非常相似,也是提供一个组件内全局的state,谁需要谁引入。无需父组件一层一层传递props。

# React.createContext

React.createContext可以创建一个局部的全局上下文,他提供的Provider和Consumer两个组件,分别作用是Provider用来包裹子组件也就是确定这个上下文的作用域,被包裹的子组件可以引入父组件暴露的这个context,并通过Consumer来消费这个context。下面一起来看看语法

父组件

import React, { createContext } from "react";
import GeneralC from "./GeneralC";
import FnC from "./FnC";
import ClassC from "./ClassC";

export const MyContext = createContext('') //此处创建context,可以传入默认值
export default function App() {
  return (
    //Provider组件接收一个value属性,这个value就是传递给子组件的props
    <MyContext.Provider value={{ name: `context's value is string!` }}>
      {/*这里写后面要进行包裹的子组件,此处先行导入后续需要消费context的3个组件*/}
      <GeneralC/>
      <hr/>
      <FnC/>
      <hr/>
      <ClassC/>
    </MyContext.Provider>
  );
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

子组件

import React, { useReducer } from "react";
import MyContext from "./main"; //从父组件里引入context

const GeneralC = () => {
  return (
    //使用Consumer包裹之后,内部可以接收到Provider的value
    <MyContext.Consumer>
      {(value) => {
        return (
          <div>
            第一种使用Context方式获取的值:{JSON.stringify(value)}
          </div>
        );
      }}
    </MyContext.Consumer>
  );
};

export default GeneralC;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

如果创建了多个上下文也是可以的,只需要一层层包裹就行了,在消费context的时候也层层包裹即可

//这里就创建了两个上下文 分别是UserContext和GoodsContext
 <UserContext.Provider value={{user,setUser}}>
                <GoodsContext.Provider value={'goods'}>
                    <Item1/>
                    <div className={styles.box}>
                        父组件的state,
                        <p>{user.name}</p>
                        <p>{user.age}</p>
                    </div>
                    <Item2/>
                </GoodsContext.Provider>
            </UserContext.Provider>
1
2
3
4
5
6
7
8
9
10
11
12
    <UserContext.Consumer>
      {(value) => (
            <GoodsContext.Consumer>
                {
                    (goods)=>(
                        <div>
                            第一种使用Context方式获取的值:{JSON.stringify(value)}
                            嵌套的Consumer{goods}
                        </div>
                    )
                }
            </GoodsContext.Consumer>
        )
      }
    </UserContext.Consumer>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

这样的写法显然并不方便,这个时候就轮到主角useContext登场了

useContext主要解决子组件在消费context时的繁琐操作

前面创建上下文传递value等操作还是不变,在消费上下文的时候不再需要使用Consumer,而是通过useContext来获取value。

import React, { useReducer,useContext } from "react";
import MyContext from "./main"; //从父组件里引入context

const value = useContext(MyContext) //在这里就可以获取到value
const GeneralC = () => {
  return (
          <div>
            第一种使用Context方式获取的值:{JSON.stringify(value)}
          </div>
  );
};

export default GeneralC;
1
2
3
4
5
6
7
8
9
10
11
12
13

这样即使有多个context,也只需要分别从useContext里取出来就行了。非常的方便

注意

所有调用了useContext的子组件都会在context发生变化时重新渲染,所以更新开销大的子组件可以使用memoization (opens new window) 来优化。

编辑 (opens new window)
上次更新: 2023/10/07, 15:57:15
关于严格模式下useEffect执行两次的问题

关于严格模式下useEffect执行两次的问题→

最近更新
01
小程序实现全局状态管理
07-09
02
前端检测更新,自动刷新网页
06-09
03
swiper渲染大量数据的优化方案
06-06
更多文章>
Theme by Vdoing | Copyright © 2023-2025 UzumakiItachi | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式