1.关于 React Hooks

React 提倡函数式编程,view = fn(props),函数更灵活,更易拆分,更易测试。尽管函数组件有着许多优势,但是函数组件太简单了,许多功能class组件能轻易实现的功能函数组件很难或者不可能实现。因次,为了让函数组件拥有更强大的能力,Hooks出现了。

关于 Hooks 介绍,无论是官方文档还是网络上都已经有了很多介绍,下面直接对常用的一些 hook 使用进行介绍。

2. State Hook

函数组件是一个纯函数,执行完就销毁了,无法存储state,如果想拥有像 class 组件中的state功能,这时就需要 state hook,把 state 功能 “钩”到纯函数中

2.1 函数组件基本“格式”

import React from 'react'const HooksT = () => {return (<><p>这是一个函数组件</p></>)
}export default HooksT;

2.2 使用useState

下面是在函数组件中实现加一减一效果,在class组件中可以通过state和setState来实现(注意,这里的state和setState是class组件中固定的唯一写法,和函数组件中的不同)

import React, { useState } from 'react'const HooksT = () => {/*** 数组解构,首先要引入:import { useState } from 'react'* 1.[count, setCount],这个可以自定义名字,但是一般来说,都是以[值,set值]这种命名方式* 2.useState(0)中的0是值的初始值,也可以是是字符串,数组,对象等* 3.如果值为对象,可以这样写:useState({a:'1',b:'2'})* 4.setCount这是可以对count进行修改的方法,如设置count值为5,则:setCount(5);设置count值加一,则:setCount(count + 1)* 5.这样的useState可以在函数组件中没有数量限制*/const [count, setCount] = useState(0)return (<><p>这是一个函数组件</p><p>{`num:${count}`}</p><button onClick={() => { setCount(count + 1) }}>num加1</button><button onClick={() => { setCount(count - 1) }}>num减1</button></>)
}export default HooksT;

3. useEffect

函数组件是一个纯函数,执行完即销毁,自己无法实现生命周期,可以用 useEffect 把生命周期 “钩” 到纯函数中,用 useEffect 模拟组件生命周期

3.1 使用 useEffect

import React, { useState, useEffect } from 'react'const HooksT = () => {const [count, setCount] = useState(0)/*** 1.模拟class组件的 DidMound 和 DidUpdate 生命周期* 当界面第一次渲染完成,或者更新时重新渲染后都会执行*/useEffect(() => {console.log('当界面第一次渲染完成,或者更新时重新渲染后都会执行')})/*** 2.仅模拟class组件的 DidMound 生命周期(在useEffect的第二个参数加一个空数组)* 当界面第一次渲染完成执行,更新时不执行*/useEffect(() => {console.log('当界面第一次渲染完成执行,更新时不执行')}, [])/*** 3.仅模拟class组件的 DidUpdate 生命周期(在useEffect的第二个参数加一个数组,数组里放入需要监听更新的值)* 当界面第一次渲染完成执行,更新时不执行* 注意1:这里useEffect中第二个参数其实是一种依赖关系,和第一点中的不同,第一点在useEffect中不加第二个参数时* 无论什么数据更新都会执行; 而此处增加 [count] 后,仅仅当count更新时才会执行,除此之外的更新不会执行。* 注意2:数组中可以放多个依赖的值,其中任何一个更新都会执行*/useEffect(() => {console.log('当界面第一次渲染完成执行,更新时不执行')}, [count])/*** 4.模拟class组件的 WillUnMount ,在销毁时执行,这一步谨慎使用* 在定义一些定时任务和全局变量时,一定要返回一个函数去进行销毁,否则会* 造成内存泄漏*/useEffect(() => {console.log('当界面销毁时执行')let timerId = window.setInterval(() => {console.log(timerId)})// 返回一个函数,模拟一个函数// 当不return时,定时任务其实不会结束,为了完全销毁,需要增加return,并返回一个函数return () => {window.clearInterval(timerId)}}, [])return (<><p>这是一个函数组件</p><p>{`num:${count}`}</p><button onClick={() => { setCount(count + 1) }}>num加1</button><button onClick={() => { setCount(count - 1) }}>num减1</button></>)
}export default HooksT;

3.2 useEffect 使用总结

1.同时模拟 componentDidMount 和 componentDidUpdate — useEffect无依赖
2.模拟 componentDidMount - useEffect 依赖 []
3.模拟 componentDidUpdate - useEffect依赖[a, b]
4.模拟 componentWillUnmount - useEffect 中返回一个函数,依赖 []

3.3 useEffect 让纯函数有了副作用

1.默认情况下,执行纯函数,输入参数,返回结果,无副作用
2.所谓副作用,就是对函数之外造成了影响,如设置全局定时任务
3.而某些时候需要副作用,则可以用 useEffect “钩” 到纯函数

3.4使用 useEffect 模拟 WillUnMount 时的注意事项

注意,模拟 WillUnMount ,但不完全相等

useEffect 中返回函数 fn:

1.useEffect 依赖 [],组件销毁时执行fn,等于 WillUnMount,
2.useEffect 无依赖或者依赖 [a, b] ,组件更新时会执行 fn,

4. 其他 Hooks

针对其他一些不常用到的 hooks 进行简单介绍:
1.useRef
2.useContext
3.useReducer
4.useMemo
5.useCallback

4.1 useRef

1.useRef常用于获取 DOM 节点

import React, { useEffect, useRef } from 'react'const HooksT = () => {const btnRef = useRef(null)useEffect(() => {console.log(btnRef.current) // btnRef所绑定的 DOM 节点}, [])return (<><p>这是一个函数组件</p><button ref={btnRef}>click</button></>)
}export default HooksT;

2.可用于保存一些其他数据

// 定义一个refconst ref = useRef()// 可以对ref进行一个赋值操作ref.current = 100;

4.2 useContext

提供了更加高级的一种组件中传递值的方式,不再需要一层一层的向下传递,而是可以隔层传递。

import React, { useContext } from 'react'// 主题颜色
const themes = {light: {foreground: '#000',background: '#eee',},dark: {foreground: '#fff',background: '#222',},
}// 创建 Context
const ThemeContext = React.createContext(themes.light)/*** 下面定义三个组件演示隔层传递*/const ThemeButton = () => {const theme = useContext(ThemeContext)return (<button style={{background: theme.background, color: theme.foreground}}>hello world</button>)
}const Toolbar = () => {return (<><ThemeButton></ThemeButton></>)
}const HooksT = () => {return (<>{/* ThemeContext.Provider */}<ThemeContext.Provider value={themes.dark}><Toolbar></Toolbar></ThemeContext.Provider></>)
}export default HooksT;

我们可以不再一层一层的去传递某些值,而是可以统一的在最顶层组件传入,然后可以在下面任何一个组件内传递,从而减少了多层组件传递的麻烦,只需要在顶层做一次修改,就能修改整个大组件,例如做主题管理。而无需再引用 Redux

4.3 useReducer

useRedecer 和 useState很像,当我们想要实现更加复杂的修改值的操作,可以使用 reducer

import React, { useReducer } from 'react'// 定义初始值
const initialSatate = {count: 0,
}// 定义修改规则
const reducer = (state, action) => {switch(action.type) {case 'increment':return {count: state.count + 1};case 'decrement':return {count: state.count - 1};default:return false;}
}const HooksT = () => {// 创建一个 reducer ,很像useStateconst [state, dispatch] = useReducer(reducer, initialSatate)return (<>count: {state.count}<button onClick={() => {dispatch({type: 'increment'})}}>increment</button><button onClick={() => {dispatch({type: 'decrement'})}}>decrement</button></>)
}export default HooksT;

useRedecer 和 redux 区别:
1.useReducer 是 useState 的代替方案,用于 state 的复杂变化
2.useReducer 是单个组件的状态管理,组件间通讯还是需要 props
3.redux 是全局的状态管理,多组件共享数据

因此,useReducer 本质上只是 useState 的升级版,并不能代替 redux

4.4 useMemo

在react中,默认情况下当父组件重新渲染时,子组件也会无条件重新渲染,当遇到性能瓶颈时,可以考虑使用 useMemo 做性能优化

1.首先要使用memo将子组件封装起来

import React, { memo } from 'react// 通过memo将子组件封装起来,即作为参数传入memo()中
const Child = memo((info) => {console.log("子组件渲染了")return (<p>子组件</p>)
})

2.在父组件中使用 useMemo 对传入子组件的数据进行一个依赖判断

import React, { useMemo } from 'react
import Child from 'path'const Parent = () => {// 通过 useMemo 来封装需要传入子组件的数据,即进行缓存数据// 数据通过return来返回cosnt info = useMemo(() = {return {name: 'jack',age: 18}},[name])        // 依赖name,只有name变化时子组件才会重新渲染console.log("父组件渲染了")return (<><p>父组件</p><Child info={info} /></>)

3. useMemo 使用总结

1.React 默认在父组件更新情况下会无条件更新子组件
2.class 组件使用SCU和PureComponent 做优化
3.Hooks 中使用 useMemo做优化
4.上面2,3点的优化原理是一样的,都是通过对 props 的一个浅层对比进行优化

4.5 useCallback

useCallback 是对 useMemo 的一个补充,针对父组件传入函数时进行缓存

只需要在 4.4 中 Parent 组件中增加对函数的缓存即可:

import React, { memo, useMemo, useCallback } from 'react
import Child from 'path'// 在这增加传入的 onChange 函数
const Child = memo(({info, onChange}) => {console.log("子组件渲染了")return (<p>子组件</p><button onClick={() => {onChange}>change</button>)
})const Parent = () => {// 通过 useMemo 来封装需要传入子组件的数据cosnt info = useMemo(() = {return {name: 'jack',age: 18}},[name])   // 将要传入子组件的函数作为参数传入 useCallback 中即可const onChange = useCallback(() => {console.log("useCallback")})console.log("父组件渲染了")return (<><p>父组件</p><Child info={info} onChange={onChange} /></>)
}

5.自定义Hook

5.1 通过定义一个axios相关的hook来演示自定义hook

安装Axios: yarn add axios --save

import { useState, useEffect } from 'react';
import axios from 'axios';/*** 封装 axios 自定义hook发送请求 */
const useAxios = (url) => {const [loading, setLoading] = useState(false)const [data, setData] = useState()const [error, setError] = useState()useEffect(() => {// 表示开始请求setLoading(true)// 利用 axios 发送网络请求axios.get(url).then(res => {setData(res)}).catch(err => {setError(err)}).finally(() => {setLoading(false)})}, [url])return [loading, data, error]
}export default useAxios;

小结:
1.本质是一个函数,命名以 use 开头
2.内部能正常使用 useState, useEffect 或者其他 Hooks
3.自定义返回结果,格式不限

5.2 第三方Hooks:

https://ahooks.js.org/zh-CN/docs/getting-started

6.Hooks 使用规范

6.1 命名规范

自定义 hooks 使用 use 开头的命名,非 hooks 尽量避免使用 use 开头命名

6.2 Hooks使用规范

1.只能用于 React 函数组件和自定义 Hook 中,其他地方不可以

2.只能用于顶层代码,不能在循环、判断(if)中使用 Hooks

3.eslint 插件 eslint-plugin-react-hook 可以检测使用规范

6.3 关于Hooks的调用顺序

函数组件,本质是一个函数,执行完即销毁;
无论是组件初始化还是组件更新,都会重新去执行这个函数(表示从销毁到再一次初始化);这一点和class组件不同,因为class组件会有组件实例,只要组件不被主动销毁,组件更新是不会导致calss组件销毁。

1.对于 useState

/*** render:初始化 state 的值* re-render: 只恢复为第一次初始化的 state 的值,不会再重新设置新的值* 只能通过 setState 修改*/
const [state, useState] = useState("name")

之所以 hooks 能到正确获取到对应的值(多个state能一一对应),是因为 hooks 是严格按顺序读取的,如果有 hooks 在 if 内,当 if 不通过时,if 内的 hooks 就不会执行,也就会导致剩下的hooks 全部错乱。

2.对于 useEffect

/*** render:添加 effect 函数* re-render:替换 effect 函数,useEffect内部的匿名函数也会重新定义*/
useEffect(() => {console.log("useEffect")
})

同样,当在 if 中使用 useEffect 时,因为执行的不确定性,也会导致前后的 useEffect 执行错乱

####hooks严格依赖于执行顺序,绝对不能在循环,判断中使用

7.React Hooks 组件逻辑复用

7.1 class组件的逻辑复用

1.Mixins 已经废弃

2.高阶组件HOC

3.Render Prop

7.2 使用 Hooks 做组件逻辑复用

1.定义一个公共的自定义hooks

2.直接在需要的地方使用即可

8.React Hooks 注意事项

1.useState 初始化值,只有第一次有效

import React, { memo, useMemo, useCallback } from 'react
import Child from 'path'const Child = memo(({info}) => {const [nameC, setNameC] = useState(info.name)return (<p>子组件</p><p>传进来 props 的 name1:{info.name}</p><p>初始化 nameC  的name2:{nameC}</p>)
})const Parent = () => {const [name, setName] = useState("name")const info = {name}return (<><p>父组件</p><Child info={info} /><button onClick={() => {setName("nameChange")}}>change</button></>)

在上面的演示代码中,当第一次运行后:name1 和 name2 值都是 ”name“,而当点击按钮修改传入的info时,name1 变成了 ”nameChange“,而name2 仍然时 ”name“

由此可以印证,useState 只有在初次渲染时会给 nameC 初始值,而在后面即使改变传入的值,useState 也不会再重新设置新的值,只能通过 setNameC来修改

2.useEffect 内部不能修改 state 的情况

使用 useEffect 时,当依赖为 [] 时,re-render 是不会重新执行 effect 函数的,这样就会导致在 useEffect 中的一些 set值 的操作没有效果

当没有依赖,或者依赖 [a, b] 中会 a 或 b 会改变的情况时,re-render 会重新执行函数,这样状态又不能保存下来

这时可以通过useRef来保存一个状态值

解决方案:


// 可以使用 useRef// 通过 useRef 为 countRef 赋值为 0
const countRef = useRef(0)// 然后可以进行修改 countRef , countRef.current 就是它的值
++countRef.current

3.useEffect 可能出现死循环

当 useEffect 的依赖里面有 {} , [] 引用类型时,会导致死循环

这是因为,react 的 useEffect 判断依赖是否变化是通过 Object.is() 这个函数,而 Object.is({}, {}) 和 Object.is([], []) 都会返回false,则 useEffect 判断为依赖变化,就会执行 effect 函数,这样就会导致死循环的发生

后续

1.webpack的完整配置流程,帮助掌握基本配置和部分高级配置
2.JavaScript版数据结构与算法,着重于实战练习
3.dva的介绍与使用

小伙伴们有需要想让作者先整理出来的可以留言评论,我会参考大家的意见来选择

React Hooks相关推荐

  1. react hooks使用_为什么要使用React Hooks?

    react hooks使用 The first thing you should do whenever you're about to learn something new is ask your ...

  2. react hooks使用_如何使用React和Hooks检测外部点击

    react hooks使用 by Andrei Cacio 通过安德烈·卡西奥(Andrei Cacio) 如何使用React和Hooks检测外部点击 (How to detect an outsid ...

  3. react hooks使用_如何使用Hooks将React类组件转换为功能组件

    react hooks使用 by Balaganesh Damodaran 通过Balaganesh Damodaran 如何使用Hooks将React类组件转换为功能组件 (How to conve ...

  4. 探React Hooks

    前言 众所周知,hooks在 React@16.8 中已经正式发布了.而下周周会,我们团队有个同学将会仔细介绍分享一下hooks.最近网上呢有不少hooks的文章,这不免激起了我自己的好奇心,想先行探 ...

  5. 使用React Hooks你可能会忽视的作用域问题

    前言 其实React Hooks已经推出来一段时间了,直到前一阵子才去尝试了下,看到的一些博客都是以API的使用居多,还有一些是对于原理的解析.而我这篇文章想写的是关于React Hooks使用中的作 ...

  6. dw按钮图片滚动js_使用 React Hooks 实现仿石墨的图片预览插件(巨详细)

    点击上方"前端教程",选择"星标" 每天前端开发干货第一时间送达! 作者:DARRELL https://juejin.im/post/5e9bf299f265 ...

  7. 通过 React Hooks 声明式地使用 setInterval

    2019独角兽企业重金招聘Python工程师标准>>> 本文由云+社区发表 作者:Dan Abramov 接触 React Hooks 一定时间的你,也许会碰到一个神奇的问题: se ...

  8. mounty不可重新挂载因为先前没有完全卸载_【译】React Hooks测试完全指南

    原文地址:https://www.toptal.com/react/testing-react-hooks-tutorial 2018年底,React在16.8版本中引入了Hooks.它们(译注:指R ...

  9. 【译】什么是React Hooks

    原文:What are React Hooks? 作者:Robin Wieruch 译者:博轩 React Hooks 于 2018年10月的React Conf 中引入,作为在 React 函数组件 ...

  10. 理解 React Hooks

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由志航发表于云+社区专栏 TL;DR 一句话总结 React Hooks 就是在 react 函数组件中,也可以使用类组件(classe ...

最新文章

  1. mysql 优化表 3000万_mysql优化:专题三、关于单表查询,可以这么优化
  2. [云炬创业基础笔记] 第三章测试4
  3. java注解编程_Java注解编程原理
  4. 深度优化LNMP之PHP
  5. ERROR 2006 (HY000) MySQL server has gone away
  6. Geotrellis系列文章链接
  7. java ctr v,解决 ctrol c ctrol v 复制 粘贴 不好用的问题 只复制一次的问题
  8. 电脑wifi距离测试软件,wifi测速工具
  9. pe下修复linux磁盘分区,找回丢失的Linux分区及Grub修复过程
  10. Linux当前目录下所有jpg文件,解决Linux平台下无法打开jpg文件(提示: “Not a JPEG file: starts with 0x89 0x50”)的方法...
  11. 东北大学《传输原理》随堂练习
  12. TCP客户端和服务端的互通信息
  13. Android音视频开发之ExoPlayer(一):快速入门ExoPlayer
  14. python语音验证码识别_基于Python的手机语音验证码api调用代码实例
  15. HDMI 之 HPD .
  16. 谷歌浏览器安全证书不受信任_使用SSL证书https协议,完美解决谷歌Chrome浏览器“不安全”...
  17. Hive 外部表的练习(多表关联查询,以及分组,子查询)
  18. 习题5-7 使用函数求余弦函数的近似值 (15分)
  19. 自定义控件其实很简单2/12
  20. 00-为什么要做骑象人--解锁Hadoop高薪之路

热门文章

  1. 计算机控制节点设备原理,CRH1型动车组计算机控制系统计算机设备及功能概述...
  2. 在unity向量空间内绘制几何(3):通过三角形重心坐标寻找三角形内任意点的位置
  3. js根据日期判断周几
  4. 威马汽车欲曲线上市:沈晖已提前持股并任职,销量垫底、员工降薪
  5. [HackerRank]Choosing White Balls
  6. CSS设置文字镂空效果
  7. createwindows返回值为NULL,GetLastError返回值是1407,但是在创建窗口之前已经注册窗口了,不知道怎么解决,求助。
  8. python functools_Python之functools模块的使用
  9. 专享策略No.1 | 震荡+趋势+动态调节
  10. java开发人员必去的知名国外网站