0


React-Hooks

1.介绍

 1-1 hooks:钩子-钩子函数:自动执行的函数
 1-2 hoos是react v16.8中新增的功能
 1-3 作用:为函数组件提供状态,生命周期等原本class组件中提供的react功能(可以理解为Hooks为函数组件购入class组件的特性)
 1-4 注意:Hooks只能在函数组件中使用

2.为什么要有Hooks

 1-1 组件状态逻辑的复用
 1-2 解决class语法记忆成本高、this指向等问题
  • 组件逻辑状态的复用:- 在 Hooks 之前,组件的状态逻辑复用经历了:mixins(混入)、HOCs(高阶组件)、render-props 等模式。- (早已废弃)mixins 的问题:1 数据来源不清晰 2 命名冲突。- HOCs、render-props 的问题:重构组件结构,导致组件形成 JSX 嵌套地狱问题。
  • class自身的问题:
    • 选择:函数组件和 class 组件之间的区别以及使用哪种组件更合适- 需要理解 class 中的 this 是如何工作的- 相互关联且需要对照修改的代码被拆分到不同生命周期函数中- componentDidMount -> window.addEventListener('resize', this.fn)- componentWillUnmount -> window.addEventListener('resize', this.fn)
  • 相比于函数组件来说,不利于代码压缩和优化,也不利于 TS 的类型推导

正是由于 React 原来存在的这些问题,才有了 Hooks 来解决这些问题

3.优势

 1-1 Hooks 只能在函数组件中使用,避免了 class 组件的问题
 1-2 复用组件状态逻辑,而无需更改组件层次结构
 1-3 根据功能而不是基于生命周期方法强制进行代码分割
 1-4 抛开 React 赋予的概念来说,Hooks 就是一些普通的函数
 1-5 具有更好的TS类型推导
 1-6 tree- - shaking友好,打包时去掉未引用的代码
 1-7 更好的压缩

项目开发中,Hooks 的采用策略:

  • 不推荐直接使用 Hooks 大规模重构现有组件
  • 推荐:新功能用 Hooks,复杂功能实现不了的,也可以继续用 class
  • 找一个功能简单、非核心功能的组件开始使用 hooks

4.useState

1.概念

 1-1 为函数组件提供状态(state)
 1-2 使用场景:当你想要在函数组件中,使用组件状态时,就要使用 useState Hook 了
//当你想要在函数组件中,使用组件状态时,就要使用 useState Hook 
 //规则 if语句/for循环中/普通函数不允许调用useState
 //react中非普通函数 1. 函数式组件 2.use开头的函数
 import React, { useState } from 'react'
 export default function App() {
     //使用useState 解构获取数据
     //第一个参数的数据 即是useState传的实参
     //setCount提供的方法 可以修改第一个参数的数据
     // 为函数组件提供状态(state)
     const [count, setCount] = useState(1)
     return (
         <div>
             {count}
             <button onClick={() => { setCount(count + 1) }}>点击改变count</button>
         </div>
     );
 }
  • 参数:状态初始值。比如,传入 0 表示该状态的初始值为 0- 注意:此处的状态可以是任意值(比如,数值、字符串等),而 class 组件中的 state 必须是对象
  • 返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState)

2.更新过程

函数组件使用 useState hook 后的执行过程,以及状态值的变化:

  • 组件第一次渲染:1. 从头开始执行该组件中的代码逻辑2. 调用 useState(0) 将传入的参数作为状态初始值,即:03. 渲染组件,此时,获取到的状态 count 值为: 0
  • 组件第二次渲染:1. 点击按钮,调用 setCount(count + 1) 修改状态,因为状态发生改变,所以,该组件会重新渲染2. 组件重新渲染时,会再次执行该组件中的代码逻辑3. 再次调用 useState(0),此时 React 内部会拿到最新的状态值而非初始值,比如,该案例中最新的状态值为 14. 再次渲染组件,此时,获取到的状态 count 值为:1

注意:useState 的初始值(参数)只会在组件第一次渲染时生效

也就是说,以后的每次渲染,useState 获取到都是最新的状态值。React 组件会记住每次最新的状态值!

3.添加多个状态

问题:如果一个函数组件需要多个状态,该如何处理? 回答:调用

useState

Hook 多次即可,每调用一次 useState Hook 可以提供一个状态。 注意:useState Hook 多次调用返回的 [state, setState] 相互之间,互不影响。

  • 使用数组解构简化 useState 的使用- 约定:修改状态的函数名称以 set 开头,后面跟上状态的名称
 // 解构出来的名称可以是任意名称
 ​
 const [state, setState] = useState(0)
 const [age, setAge] = useState(0)
 const [count, setCount] = useState(0)

4.状态读取和修改

状态的使用:1 读取状态 2 修改状态

  1. 读取状态:该方式提供的状态,是函数内部的局部变量,可以在函数内的任意位置使用
  2. 修改状态:
  • setCount(newValue) 是一个函数,参数表示:新的状态值
  • 调用该函数后,将使用新的状态值替换旧值
  • 修改状态后,因为状态发生了改变,所以,该组件会重新渲染

5.使用规则

 1-1 React Hooks 只能直接出现在 函数组件 中,不能嵌套在 if/for/其他普通函数中!
 1-2 React Hooks 必须要每次组件渲染时,按照相同的顺序来调用所有的 Hooks。
 为什么会有这样的规则? 因为 React 是按照 Hooks 的调用顺序来识别每一个 Hook,如果每次调用的顺序不同,导致 React 无法知道是哪一个 Hook

5.useEffect

1.side Effect--副作用

 1-1 使用场景:当你想要在函数组件中,处理副作用(side effect)时,就要使用 useEffect Hook 了
 1-2 作用:处理函数组件中的副作用(side effect)

理解:副作用是相对于主作用来说的,一个功能(比如,函数)除了主作用,其他的作用就是副作用 对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)

React 组件的公式:UI = f(state)

常见的副作用(side effect)

  • 数据(Ajax)请求、手动修改 DOM、localStorage 操作等
// 不带副作用的情况:
 // 该函数的(主)作用:计算两个数的和
 function add(a, b) {
   return a + b
 }
 ​
 // 带副作用的情况:
 let c = 1
 function add(a, b) {
   // 因为此处修改函数外部的变量值,而这一点不是该函数的主作用,因此,就是:side effect(副作用)
   c = 2
   return a + b
 }
 ​
 // 带副作用的情况:
 function add(a, b) {
   // 因为 console.log 会导致控制台打印内容,所以,也是对外部产生影响,所以,也是:副作用
   console.log(a)
   return a + b
 }
 ​
 // 没有副作用:
 function getName(obj) {
   return obj.name + Math.random()
 }
 ​
 // 有副作用:
 function getName(obj) {
   // 此处直接修改了参数的值,也是一个副作用
   obj.name = '大飞哥'
   return obj.name
 }
 const o = { name: '小马哥' }
 fn(o)

2.基本使用

使用场景:当你想要在函数组件中,处理副作用(side effect)时,就要使用 useEffect Hook 了

作用:处理函数组件中的副作用(side effect) 注意:在实际开发中,副作用是不可避免的。因此,react 专门提供了 useEffect Hook 来处理函数组件中的副作用

useEffect

Hook 看做

componentDidMount

componentDidUpdate

componentWillUnmount

这三个函数的组合。

//useEffect初体验
 import React, { useState, useEffect } from 'react'
 export default function App() {
     const [count, setCount] = useState(1)
     //调用useEffect  useEffect是一个自动执行的钩子函数 生命周期的阶段会执行
     //语法:useEffect(回调函数)
     useEffect(() => {
         document.title = count
         //挂载+更新+销毁的时候都会自动执行
         console.log('我被执行了');
     })
     return (
         <div>
             {count}
             <button onClick={() => { setCount(count + 1) }}>点击改变count</button>
         </div>
     );
 }

解释:

  • 参数:回调函数(称为 effect),就是在该函数中写副作用代码
  • 执行时机:该 effect 会在每次组件更新(DOM更新)后执行

3.依赖项

 1. []代表挂载时 只执行一次 
     使用场景:发请求 监听事件 开启定时器
     useEffect(() => {
         document.title = count
         console.log('我被执行了');
     }, [])
     2. [依赖项] 代表:挂载时和更新时二合一,相当于componentDidMount和componentDidUpdate二合一 
     只会监听依赖项的改变 非依赖项不会触发  使用场景:缓存
     3. 不传
     不同:监听全部依赖项的变化
     数据大时 会导致卡顿
//1.不传时
 import React, { useState, useEffect } from 'react'
 export default function App() {
     const [count, setCount] = useState(1)
     //调用useEffect  useEffect是一个自动执行的钩子函数 生命周期的阶段会执行
     //语法:useEffect(回调函数)
     useEffect(() => {
         document.title = count
         console.log('我被执行了');
     })
     return (
         <div>
             {count}
             <button onClick={() => { setCount(count + 1) }}>点击改变count</button>
         </div>
     );
 }
//2.传空数组时
 //useEffect初体验
 import React, { useState, useEffect } from 'react'
 export default function App() {
     const [count, fn] = useState(1)
     //调用useEffect  useEffect是一个自动执行的钩子函数 生命周期的阶段会执行
     //语法:useEffect(回调函数)
     // 1. []代表挂载时 只执行一次 
     //使用场景:发请求 监听事件 开启定时器
      useEffect(() => {
          document.title = count
          console.log('我被执行了');
      }, [])
     return (
         <div>
             {count}
             <button onClick={() => { fn(count + 1) }}>点击改变count</button>
         </div>
     );
 }
//3.[依赖项]
 //useEffect初体验
 import React, { useState, useEffect } from 'react'
 export default function App() {
     const [count, fn] = useState(1)
     //调用useEffect  useEffect是一个自动执行的钩子函数 生命周期的阶段会执行
     //语法:useEffect(回调函数)
     // 2. [依赖项] 代表:挂载时和更新时二合一,相当于componentDidMount和componentDidUpdate二合一 
     // 只会监听依赖项的改变 非依赖项不会触发  使用场景:缓存
     useEffect(() => {
         document.title = count
         console.log('我被执行了');
     }, [count])//类似vue中的侦听器 开了immediately
     return (
         <div>
             {count}
             <button onClick={() => { fn(count + 1) }}>点击改变count</button>
         </div>
     );
 }

4.监听多个状态

 // 1、第一种写法:分开监听
    useEffect(() => {
      console.log('每次拿到最新的count', count);
    }, [count]);
 ​
   useEffect(() => {
      console.log('每次拿到最新的count2', count2);
   }, [count2]);
 ​
   // 2. 第二种写法:数组内写多个变量
   useEffect(() => {
     console.log('每次拿到最新的count 和 count2', count + count2);
   }, [count, count2]);

5.发送请求

准备工作:

  1. 安装axios
  2. 准备utils/request.js
  3. 可能需要重启项目。

在组件中,使用 useEffect Hook 发送请求获取数据(side effect):

// ✅正确写法
 useEffect(() => {
   const loadData = async () => {
     await ...
   
   }
   loadData()
 }, [])
 ​
 // ❌错误演示:
 // 不要给 useEffect 第一级函数添加 async
 useEffect(async () => {}, [])

解释:

  • 注意:useEffect 只能是一个同步函数,不能使用 async
  • 因为 effect 的返回值应该是一个清理函数,React 会在组件卸载或者 effect 的依赖项变化时重新执行
  • 但如果 effect 是 async 的,此时返回值是 Promise 对象。这样的话,就无法保证清理函数被立即调用
  • 如果延迟调用清理函数,也就没有机会忽略过时的请求结果或取消请求
  • 为了使用 async/await 语法,可以在 useEffect 内部或外部创建 async 函数,并调用

6.组件挂载+卸载时

import React, { useState, useEffect } from 'react'
 export default function App() {
     const [isShow, setIsShow] = useState(true)
     return (
         <div>
             <button onClick={() => setIsShow(!isShow)}>点我</button>
             {isShow && <Child></Child>}
         </div>
     );
 }
 function Child() {
     //在子组件挂载的时候监听窗口改变事件
     const reziseFn = () => {
         console.log('我被触发了');
     }
     useEffect(() => {
         //开启监听事件
         window.addEventListener('resize', reziseFn)
         //useEffect接收的回调函数中 返回一个函数 代表卸载是执行该函数
         return () => {
             //子组件卸载时触发
             console.log('我被卸载了');
             //清除监听事件
             window.removeEventListener('resize', reziseFn)
         }
     }, [])
 ​
     return (
         <div>我是子组件</div>
     );
 }

解释:

  • effect 的返回值也是可选的,可省略。也可以返回一个清理函数,用来执行事件解绑等清理操作
  • 清理函数的执行时机:- 1【空数组没有依赖】组件卸载时- 此时,相当于 class 组件的 componentWillUnmount 钩子函数的作用- 2 【有依赖项】effect 重新执行前(暂时知道即可)
  • 推荐:一个 useEffect 只处理一个功能,有多个功能时,使用多次 useEffect
  • 优势:- 根据业务逻辑来拆分,相同功能的业务逻辑放在一起,而不是根据生命周期方法名称来拆分代码- 编写代码时,关注点集中;而不是上下翻滚来查看代码

7.useEffect踩坑注意

1. useEffect第二个参数,需要先声明依赖项

export default function App() {
   // ❌错误写法,type 和 list 还没创建,无法使用
   useEffect(() => {
     console.log('数据更新时 - 设置本地存储数据')
   }, [list, type])  
   const [list, setList] = useState([])
   const [type, setType] = useState('all')

2.书写多个 useEffect 时,注意先后书写顺序。(先书写组件挂载时,再书写组件更新时)

 // 数据更新时 - 设置本地存储数据
   useEffect(() => {
     localStorage.setItem('todos', JSON.stringify(list))
     localStorage.setItem('todos-type', type)
   }, [list, type])
 ​
   // ❌错误写法:组件挂载时应该要写在更新时前面。
   useEffect(() => {
     const list = JSON.parse(localStorage.getItem('todos')) || []
     const type = localStorage.getItem('todos-type') || 'all'
     setList(list)
     setType(type)
   }, [])

6.useRef

 1-1 作用:保存数据 获取DOM 获取组件实例对象
 1-2 useRef和React.createRef完全相同:创建Ref
 //useEffect初体验
 import React, { useState, useEffect, useRef } from 'react'
 export default function App() {
     const [isShow, setIsShow] = useState(true)
     return (
         <div>
             <button onClick={() => setIsShow(!isShow)}>点我</button>
             {isShow && <Child></Child>}
         </div>
     );
 }
 function Child() {
     //     ref作用:3. 保存数据 1. 获取DOM  2 获取组件实例对象
     //   🔔useRef的作用和React.createRef完全相同:创建ref
     // 1。 创建ref
     const timeRef = useRef()
     console.log(timeRef);
     useEffect(() => {
         // 挂载时,开启定时器 使用ref保存数据
         timeRef.current = setInterval(() => {
             console.log('我计时了');
         }, 1000)
         // 卸载时,卸载定时器
         return () => {
             clearInterval(timeRef.current)
             console.log('我被停止了');
         }
     }, [])
 ​
 ​
     return (
         <div>我是子组件</div>
     );
 }

7.useHistory

 1-1 作用:创建htistory对象
 1-2 用途:操纵history跳转路由
//引入实例对象
 import { useHistory } from "react-router-dom";
 function HomeButton() {
     //创建history
   const history = useHistory();
   function handleClick() {
       //跳转路由
     history.push("/home");
   }
   return (
     <button type="button" onClick={handleClick}>
       Go home
     </button>
   );

8.useParams

 1-1 作用:获取动态路由的参数

9.useRouteMatch

 1-1 作用:获取match对象

10.useLocation

 1-1 作用:获取location对象

本文转载自: https://blog.csdn.net/aa18520155579/article/details/123020480
版权归原作者 无情的代码机器aa 所有, 如有侵权,请联系我们删除。

“React-Hooks”的评论:

还没有评论