React Hooks 常用钩子及基本原理

一. 简述

在以往的react16.8前, 我们React组件创建方式基本包含两种方式: 类组件, 纯函数组件。 React 团队希望组件最佳写法应该是函数,而不是类。
然而常规的纯函数组件有以下特点:

  • 没有状态
  • 没有生命周期
  • 没有 this

因存在如上特点,使得纯函数组件只能做UI展示的功能, 涉及到状态的管理与切换就不得不用到类组件或这redux。 但因为简单的页面也是用类组件,同时要继承一个React实例,使得代码会显得很重。

‘Hooks’ 单词意思为:钩子。
React Hooks 意思是:组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码“钩”进来。
React Hooks常用钩子有如下四种:

  • useState()
  • useContext()
  • useReducer()
  • useEffect()

使用hooks 我们会发现没有了继承,渲染逻辑,生命周期等, 代码看起来更加的轻便简洁了。
React 约定,钩子一律使用 use 前缀命名 (自定义钩子都命名为:useXXXX)

二. 常用举例及原理分析

useState 状态钩子

官方示例:

import React, {useState} from 'react'
const AddCount = () => {
  const [ count, setCount ] = useState(0)
  const addcount = () => {
    let newCount = count
    setCount(newCount+=1)
  } 
  return (
    <>
      <p>{count}</p>
      <button onClick={addcount}>count++</button>
    </>
  )
}

原理:

// 我们实现一个简易版的useState
let memoizedStates = [ ]  // 多个useState 时需要使用数组来存
let index = 0
function useState (initialState) {
   memoizedStates[index] = memoizedStates[index] || initialState
   let currentIndex = index;
   function setState (newState) {
      memoizedStates[currentIndex] = newState
      render()
   }
   return [memoizedStates[index++], setState]
}

useContext 共享状态钩子

该钩子的作用是,在组件之间共享状态。 可以解决react逐层通过Porps传递数据。

import React,{ useContext } from 'react'
const ShareContext= () => {
  const AppContext = React.createContext({})
  const A =() => {
    const { name } = useContext(AppContext)
    return (
        <p>我是A组件的名字{name}<span>我是A的子组件{name}</span></p>
    )
}
const B =() => {
  const { name } = useContext(AppContext)
  return (
      <p>我是B组件的名字{name}</p>
  )
}
  return (
    <AppContext.Provider value={{name: 'hook测试'}}>
    <A/>
    <B/>
    </AppContext.Provider>
  )
}
export default ShareContext

useContext(context) 是针对 context (上下文) 提出的api。 它接受React.createContext()的返回结果作为参数(也就是context对象并返回最近的context)。 使用useContext将不再需要Provider 和 Consumer。当最近的context更新时,那么使用该context的hook将会重新渲染。

useReducer(): Action 钩子

React本身不提供状态管理。 而useReducer() 提供了状态管理。其基本原理是通过用户在页面中发起action, 从而通过reducer方法来改变state, 从而实现页面和状态的通信。

import React,{useReducer} from 'react'

const MyReducer= () => {
const reducer = (state, action) =>  {
 if(action.type === ''add){
  return {
  ...state,
  count: state.count +1,
  }
 }else {
   return state
  }
 }
const addcount = () => { 
  dispatch({
    type: 'add'
  })
 }
const [state, dispatch] = useReducer(reducer, {count: 0})
return (
<>
<p>{state.count}</p>
<button onClick={addcount}>count++</button>
</>
)
}
export default MyReducer

使用useReducer() 代替了Redux的功能, 但useReducer 无法提供中间件等功能。

useEffect(): 副作用钩子

React常规开发中,我们习惯将一些处理副作用的操作,如:异步请求等 放在特定的生命周期中。 useEffect 则是为函数组件提供了副作用的钩子。
useEffect() : 接收两个参数, 第一个是进行的异步操作, 第二个是数组,用来给出Effect的依赖项。第二个参数(数组)发生变化, ==useEffect()==就会执行。第二项不填时, useEffect() 会在每次组件渲染时执行。

import React, { useState, useEffect } from 'react'

const AsyncPage = ({name}) => {
const [loading, setLoading] = useState(true)
const [person, setPerson] = useState({})
//  useEffect 类似compomnetDidMount 初始化会执行,更新时会执行
  useEffect(() => {
    setLoading(true)
    setTimeout(()=> {
      setLoading(false)
      setPerson({name})
    },3000)
  },[name])
  return (
    <div>
      {loading?<p>Loading...</p>:<p>{person.name}</p>}
    <div/>
  )
}

const PersonPage = () =>{
  const [state, setState] = useState('')
  const changeName = (name) => {
    setState(name)
  }
  return (
    <>
      <AsyncPage name={state}/>
      <button onClick={() => {changeName('名字1')}}>名字1</button>
      <button onClick={() => {changeName('名字2')}}>名字2</button>
    </>
  )
}

export default PersonPage 

创建自己的Hooks

有时候我们需要创建自己想要的Hooks, 来满足更便捷的开发。

import React, { useState, useEffect } from 'react'

const usePerson = (name) => {
const [loading, setLoading] = useState(true)
const [person, setPerson] = useState({})

  useEffect(() => {
    setLoading(true)
    setTimeout(()=> {
      setLoading(false)
      setPerson({name})
    },2000)
  },[name])
  return [loading,person]
}

const AsyncPage = ({name}) => {
  const [loading, person] = usePerson(name)
    return (
      <div>
        {loading?<p>Loading...</p>:<p>{person.name}</p>}
      </div>
    )
  }

const PersonPage = () =>{
  const [state, setState]=useState('')
  const changeName = (name) => {
    setState(name)
  }
  return (
    <div>
      <AsyncPage name={state} />
      <button onClick={() => {changeName('名字1')}}>名字1</button>
      <button onClick={() => {changeName('名字2')}}>名字2</button>
    </div>
  )
}

export default PersonPage 

上面自定义了一个Hooks, 它接受一个字符串, 返回一个数组, 数组中的两个状态在使用usePerson() 时, 会根据传入的不同反而返回不同的状态。

React Hooks 概要及未来

根据官方文档的说法:

  • 完全可选
  • 100%向后兼容 (Hook 不会含任何破坏性改动)
  • 现在可用 (Hook 已发布于 v16.8.0)
  • 没有计划从React中移除class
  • Hook不会影响对React概念的理解 (Hook 为React提供了相关API:props, state,context, refs以及生命周期)
  • 16
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React HooksReact 16.8 版本引入的一种新特性,它是为了使函数组件能够拥有状态和其他 React 特性而设计的。 React Hooks原理基于两个核心概念:闭包和钩子函数。 1. 闭包:在函数组件内部,可以通过闭包的方式引用外部作用域的变量。React Hooks 利用了闭包的特性,使得可以在函数组件内部存储和更新状态。 2. 钩子函数:React Hooks 提供了一系列的钩子函数,如 useState、useEffect、useContext 等。这些钩子函数是预定义的特殊函数,可以在函数组件中使用,通过调用这些钩子函数,可以获取和操作组件的状态、副作用和上下文等。 当一个组件使用了 React HooksReact 在底层会创建一个与该组件实例相关联的 Fiber 对象,并在组件渲染时执行组件函数。在执行组件函数时,React 跟踪每个组件函数内部所有的钩子函数调用,并将其与该组件实例相关联。 当组件函数执行时,如果遇到 useState 钩子调用,React 会查找该钩子函数对应的状态值,并将其返回给组件函数。组件通过 useState 返回的状态值可以读取和更新组件的状态。 当组件函数执行完毕后,React 会将该组件的状态和副作用存储在 Fiber 对象中,并将 Fiber 对象添加到更新队列中。之后,React 会根据更新队列中的 Fiber 对象,对组件进行批量更新,实现页面的重新渲染。 通过这种方式,React Hooks 实现了函数组件的状态管理和副作用处理,使得开发者可以更方便地编写和维护 React 组件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值