React系列-轻松学会Hooks(中)

本文是React Hooks系列的中篇,详细介绍了useState、useEffect、useRef、useCallback和useMemo等核心Hooks的使用和原理。通过实例解析了如何在函数组件中实现状态管理和副作用处理,帮助读者掌握React Hooks的精髓。
摘要由CSDN通过智能技术生成

前端学习笔记📒

最近花了点时间把笔记整理到语雀上了,方便同学们阅读:公众号回复笔记或者简历

在这里插入图片描述

开头

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一变化,就重新执行,会重复声明,所以这也是为什么需要useMemouseCallBack这两个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) 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值