前端学习笔记📒
最近花了点时间把笔记整理到语雀上了,方便同学们阅读:
公众号回复笔记或者简历
开头
1.看到这里了就点个在看支持下吧,你的「点赞,在看」
是我创作的动力。
2.关注公众号前端壹栈,回复「1」加入前端交流群
!「在这里有好多前端开发者,会讨论前端知识,互相学习」!
3.也可添加公众号【前端壹栈】
,一起成长
我们在第一篇中介绍了
Mixin HOC Render Props
,接下来来介绍另外一种非常重要的状态逻辑复用模式
,那就是react-hooks
React系列-Mixin、HOC、Render Props(上)
React系列-轻松学会Hooks(中)
React系列-自定义Hooks很简单(下)
HOC、Render Props、组件组合、Ref 传递……代码复用为什么这样复杂?,
根本原因在于细粒度代码复用不应该与组件复用捆绑在一起
也就是我们前面所说的这些模式是在既有(组件机制的)游戏规则下探索出来的上层模式
❗️❗️HOC、Render Props 等基于组件组合的方案
,相当于先把要复用的逻辑包装成组件
,再利用组件复用机制实现逻辑复用。
自然就受限于组件复用,因而出现扩展能力受限、Ref 隔断、Wrapper Hell……等问题
🤔直接的代码复用方式
想想在我们平时开发中,我们要复用一段逻辑是不是抽离出一个函数,比如用到的防抖函数、获取token函数
但是对于react的复用逻辑不同
,在没有hooks出来之前,函数是内部是无法支持state的,所以抽离成函数的模式好像是办不到,实际也可以做到的
// 设置提示语tip
export const setTip = function (context: any) {
// vote-tip提示
const tipVisible = JSON.parse(localStorage.getItem('tipVisible') as string)
if (Object.is(tipVisible, null)) {
localStorage.setItem('tipVisible', 'true')
} else if (Object.is(tipVisible, false)) {
context.setState({
tipVisible: false
})
}
}
比如笔者在业务开发中尝试把关联到state复用逻辑
像基本工具函数一样单独抽离出来,这里的context
实际就是当前组件,也就是我通过this
去让函数支持了state
,但是这样的代码很难维护,因为 你可能找不到它们的关联性
hooks应运而生
从Mixin、HOC 、Render Props模式解决状态逻辑复用问题,但是没有去根本的解决复用问题,函数应是代码复用的基本单位,而不是组件
,所以说为甚么hook是颠覆性的
,因为它从本质上解决了状态逻辑复用问题,以函数作为最小的复用单位
,而不是组件
什么是 Hook?
官方介绍:Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
什么是函数组件
函数组件只是一个执行函数取返回值的过程,简单理解:
state变化,函数组件执行,触发render更新视图
,跟Class组件还是不一样的,类组件是state变化,触发render方法更新而不是
,这代表什么❓,代表类组件的属性不会被重复声明,而函数组件每次state一变化,就重新执行,会重复声明,所以这也是为什么需要useMemo
和useCallBack
这两个hook,我们接下来会讲到
const Counter=()=>{
const [
number,
setNumber
] = useState(0)
console.log("我触发执行了")
return (
<>
<p>{
number}</p>
<button
onClick={
() => setNumber(number + 1)
}
>
改数字
</button>
</>
)
}
另外一个有意思的点是:开发中如果我们使用类组件那么就要跟this
打交道,然而使用了Hook帮我们摆脱了this场景问题
,但是又引入了一个问题,你使用了函数,那么自然而然就会跟闭包
打交道,有什么你会不知不觉陷入闭包陷阱
(接下来会说到),挺神奇的羁绊
,但是闭包
带来的好处太多了
记忆函数or缓存函数❓
react-hook的实现离不开
记忆函数(也称做缓存函数)
或者应该说得益于闭包
,我们来实现一个记忆函数
吧
const memorize = function(fn) {
const cache = {
} // 存储缓存数据的对象
return function(...args) {
// 这里用到数组的扩展运算符
const _args = JSON.stringify(args) // 将参数作为cache的key
return cache[_args] || (cache[_args] = fn.apply(fn, args)) // 如果已经缓存过,直接取值。否则重新计算并且缓存
}
}
测试一下:
const add = function(a) {
return a + 1
}
const adder = memorize(add)
adder(1) // 2 cache: { '[1]': 2 }
adder(1) // 2 cache: { '[1]': 2 }
adder(2) // 3 cache: { '[1]': 2, '[2]': 3 }
useState
为什么使用
开发中我们会经常遇到,当我们一个函数组件想要有自己维护的state的时候,不得已只能转换成
class
useState 的出现是 :useState 是允许你在 React 函数组件中添加 state 的 Hook 简单的讲就是:可以让你在在函数组件里面使用 class的setState
如何使用
useState接受一个参数,返回了一个数组
// 使用es6解构赋值,useState(0)的意思是给count赋予初始值0
// count是一个状态值,setCount是给这个状态值进行更新的函数
const [count, setCount] = useState(0);
举个例子🌰:
import React, {
useState } from 'react'
function Example() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {
count} times</p>
<button onClick={
() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
知识点合集
初始化值
useState的初始值,只在第一次有效
场景;点击按钮更新子组件的count
const Child = ({
data}) =>{
const [count, setCount] = useState(data)
return (
<div>
<div>child</div>
<div>count:{
count} --- data:{
data}</div>
</div>
);
}
const Parent =()=>{
const [data, setData] = useState(0)
return(
<div>
<div>
{
data}
</div>
<button onClick={
()=>setCount(data+1)}>更新data</button>
<Child data={
data}/>
</div>
)
}
测试一下:
// 点击按钮
<div>count:0 --- data:1</div>
更新是直接替换
useState返回更新state的函数
与class 组件的 this.setState不同,它不会把新的 state 和旧的 state 进行合并,而是直接替换
,相当于直接返回一个新的对象
,所以这也是闭包陷阱产生的原因之一
let testUser={
name:"vnues"} // 定义全局变量
const Parent =()=>{
const [user, setUser] = useState(testUser)
if(user!==testUser){
testUser=user
console.log(testUser)
}
return(
<div>
<button onClick={
()=>setCount({
age:18})}>更新data</button>
<Child data={
data}/>
</div>
)
}
测试一下:
// 点击按钮
testUser:{
age:18}
可以看到,函数运行是进入if条件里的,这说明什么,说明user和testUser的指向不同了
,证明是直接替换
useState原理
一般而言,函数重新执行,代表着重新初始化状态以及声明,那么我就很好奇,函数组件的
hook
是如何保存上一次的状态,来看看它的原理吧
let memoizedStates = [] // 存储state
let index = 0
function useState (initialState) {
// 判断memoizedStates有没有缓存值,没有则还是个初始化的useState
memoizedStates[index] = memoizedStates[index] || initialState
let currentIndex = index
function setState (newState)