1、什么是hook?
react hook是react 16.8推出的方法,能够让函数式组件像类式组件一样拥有state、ref、生命周期等属性。

2、为什么要出现hook?
函数式组件是全局当中一个普通函数,在非严格模式下this指向window,但是react内部开启了严格模式,此时this指向undefined,无法像类式组件一样使用state、ref,函数式组件定义的变量都是局部的,当组件进行更新时会重新定义,也无法存储,所以在hook出现之前,函数式组件有很大的局限性,通常情况下都会使用类式组件来进行代码的编写。

3、有哪些常用的hook?
(1) useState
使函数式组件也能保存状态的一个hook,这个hook的入参是状态的初始值,返回值是一个数组,数组里第一个参数为状态的值,第二个参数为修改状态的方法。

// 初始化
const [ count, setCount ] = useState(0)
// 更新
setCount(count+1)

(2) useEffect

函数式组件用来模拟生命周期的hook,可以模拟组件挂载完成、更新完成、即将卸载三个阶段,即componentDidMount、componentDidUpdate、componentWillUnmount。

useEffect的一个参数为函数,表示组件挂载、更新时执行的内容,在函数里再返回一个函数,表示组件即将卸载时调用的函数。

第二个参数为可选项,可传入数组,数组里可以为空,表示不依赖任何状态的变化,即只在组件即将挂载时执行,后续任何状态发生了变化,都不调用此hook。数组里也可以定义一或多个状态,表示每次该状态变化时,都会执行此hook。

useEffect(()=>{
  // 这样模拟的是 componentDidMount
}, []) useEffect(()=>{
  // 这样模拟的是componentDidMount 以及当count发生变化时执行componentDidUpdate
}, [count]) useEffect(()=>{
  return ()=>{
    // 这样模拟的是 componentWillUnmount
 }
}, [])

(3) useContext
在没有hook之前,我们通常都会通过 xxxContext.Provider 和 xxxContext.Consumer 的方式来传递和获取context的值,使用hook之后,传递context的方式不变,但子元素获取context的方式变得更加的简洁。

// 以前的定义方式
const CountContext = React.createContext()
<CountContext.Provider value={{ count: 10 }}>
<...自定义的组件>
</CountContext.Provider> // 子元素
<CountContext.Consumer>
{ value => { console.log(value.count) }} //10
</CountContext.Consumer> //使用context的获取方式
const countObj = useContext(CountContext)
console.log(countObj.count) // 10

(4) useRef

useRef和类式组件中createRef用法比较类似,返回一个ref对象,这个对象在函数的整个生命周期都不变,根据这个特性,有两种比较常见的用法。

① 用于dom元素或者组件上,通过current属性可以获取到dom元素或者类式组件的实例对象。需要注意的是,无论是useRef还是createRef或者是回调形式、字符串形式的ref,都是不能直接给函数式组件定义的,因为函数式组件的this指向undefined,没有实例对象,只能通过forwardRef定义到函数式组件中的某个dom元素。

// 这样就将传递给函数式组件的ref绑定在了函数式组件内部的input标签上
import React, { useRef, forwardRef } from 'react' // 使用函数表达式的方式定义了一个函数式组件
const InputCom = forwardRef((props, ref) => {
  return <input ref={ref}/>
}) export default function refDemo(){
const comRef = useRef()
  return(<div>
     <InputCom ref={comRef}/>
  </div>)
}

② 保存一个数据,该数据如果不手动修改,它在整个生命周期中都不变

const [ count, setCount ] = useState(0)
const prevCount = useState(count)
// 当count发生变化时,组件更新,对count的前一次数据进行保存
useEffect(()=>{
  prevCount.current = count
}, [count])

(5) useReducer
useReducer相当于是useState的升级版,作用与useState类似,都是用来保存状态,但它的不同点在于可以定义一个reducer的纯函数,来处理复杂数据。

// 定义一个处理数据的reducer纯函数
function reducer(prevState, action){
  switch(action.type){
    case 'increment':
      return {...prevState, count: prevState.count + 1 }
    case 'decrement':
      return {...prevState, count: prevState.count - 1 }
    default:
    return prevState
  }
} // 初始化状态
const [ count, dispatch ] = useReducer(reducer, { count: 0 })
// 修改状态,此时的修改需要派发一个action,让传入的reducer函数进行处理
dispatch({ type: 'increment' })

(6) useCallback
函数式组件中,每一次更新状态,自定义的函数都要进行重新的声明和定义,如果函数作为props传递给子组件,会造成子组件不必要的重新渲染,有时候子组件并没有使用到父组件发生变化的状态,此时可以使用useCallback来进行性能优化,它会为函数返回一个记忆的值,如果依赖的状态没有发生变化,那么则不会重新创建该函数,也就不会造成子组件不必要的重新渲染。

import React, { useState, useCallback, memo } from 'react'
const AddBtn = memo((props)=>{ // 使用函数表达式的方式定义了一个函数式组件
return<button onClick={props.increment}>+1</button>
}) export default function CallBackPerformance(){
const [ count, setCount ] = useState(0)
const [ show, setShow ] = useState(true)
const increment1 = () => {
console.log('increment1被调用了')
setCount(count+1)
  } const increment2 = useCallback(()=>{ // 使用了useCallback来优化的函数
console.log('increment2被调用了')
setCount(count+1)
},[count]) return(<div>
<div>当前计数:{count}</div>
<AddBtn increment={increment1} name="1"/>
<AddBtn increment={increment2} name="2"/>
<button onClick={e => setShow(!show)}>切换show</button>
</div>)
}
// 当show这个状态发生变化时,子组件increment1会重新渲染,increment2不会重新渲染

(7) useMemo

useMemo也是返回一个记忆的值,如果依赖的内容没有发生改变的话,这个值也不会发生变化,useMemo与useCallback的不同点在于useMemo需要在传入的函数里需要return 一个值,这个值可以是对象、函数,格式如下。

useMemo(()=>{
return { count }
}, [count]) // 使用useCallback时
const increment2 = useCallback(()=>{
  setCount(count+1)
},[count]) // 使用useMemo模拟useCallback
const increment2 = useCallback(()=>{
  return ()=>{
    setCount(count+1)
  }
},[count]) // useMemo的应用场景,当要进行一些复杂的计算时,
//计算的值没有发生变化,并不需要每一次更新都重新计算 import React, { useState, useMemo } from 'react'
const calculateNum = (count) => {
console.log('total重新计算了')
let total = 0
for(let i = 0; i <= count; i++){
total += i
}
return total
} export default function ComplexUseMemo(){
const [ count, setCount ] = useState(10)
const [ show, setShow ] = useState(true)
const total = useMemo(()=>{
return calculateNum(count)
}, [count])
return(<div>
<div>{total}</div>
<button onClick={e=>setCount(count+1)}>+1</button>
<button onClick={e=>setShow(!show)}>切换show</button>
</div>)
}

(8) useImperativeHandle
这个是与forwardRef配合来使用的,当我们对函数式组件使用forwardRef将ref指定了dom元素之后,那就父组件就可以任意的操作指定的dom元素,使用useImperativeHandle就是为了控制这样的一种行为,指定父元素可操作的子元素的方法。

import React, {  useRef, useImperativeHandle, forwardRef } from 'react'
const InputComp = forwardRef((props, ref)=>{
const childInputRef = useRef()
useImperativeHandle(ref, ()=>({
focus: ()=>{
childInputRef.current.focus()
}
}), [childInputRef.current]) return<input ref={childInputRef}></input>
}) export default function ImperativeHookDemo() {
const inputRef = useRef()
return(<div>
<InputComp ref={inputRef}/>
<button onClick={e=>inputRef.current.focus()}>聚焦</button>
</div>)
}

(9) useLayoutEffect

这个方法与useEffect类似,只是执行的顺序稍有不同,useEffect是在组件渲染绘制到屏幕上之后,useLayoutEffect是render和绘制到屏幕之间。

4、如何自定义hook?
hook只能定义在函数式组件中,不能在普通函数中使用,如果我们想要使用到上面的hook来封装一些方法供很多个组件调用,这时候就需要自定义hook,自定义hook的命名就是在函数名前加 use,函数名由 saveInfo 改为 useSaveInfo 即可。

react中常见hook的使用方式与区别的更多相关文章

  1. css中常见几种float方式以及倒计时(刷新页面不清)

    css中常见几种float方式 http://jingyan.baidu.com/article/72ee561a670269e16138dfd5.html <script type=" ...

  2. React中组件间通信的方式

    React中组件间通信的方式 React中组件间通信包括父子组件.兄弟组件.隔代组件.非嵌套组件之间通信. Props props适用于父子组件的通信,props以单向数据流的形式可以很好的完成父子组 ...

  3. ajax中Post和Get请求方式的区别?

    ajax中Post和Get请求方式的区别: 1.Post传输数据时,不需要在URL中显示出来,而Get方法要在URL中显示. 2.Post传输的数据量大,可以达到2M,而Get方法由于受到URL长度的 ...

  4. 整理下react中常见的坑

    其实有些也不能算是坑,有些是react的规定,或者是react的模式和平常的js处理的方式不同罢了 1.setState()是异步的this.setState()会调用render方法,但并不会立即改 ...

  5. react创建组件的几种方式及其区别

    react创建组件有如下几种方式 ①.函数式定义的无状态组件 ②.es5原生方式React.createClass定义的组件   ③.es6形式的extends React.Component定义的组 ...

  6. React创建组件的三种方式及其区别

    内容转载于http://www.cnblogs.com/wonyun/p/5930333.html React推出后,出于不同的原因先后出现三种定义react组件的方式,殊途同归; 具体的三种方式: ...

  7. Objective-C 中self.与_访问方式的区别

    Objective-C中属性self.a与_a访问的区别: 在OC中我们可以通过指令@property定义属性. OC对属性封装了许多方法,同时也会自动实现一些方法,相比实例变量,感觉更加面向对象些. ...

  8. 浅谈HTTP中GET和POST请求方式的区别

    浅谈HTTP中GET和POST请求的区别 HTTP认知: HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议.HTTP的底层是TCP/IP.所以GET和POST的底层也是TCP/IP,也 ...

  9. React中组件之间通信的方式

    一.是什么 我们将组件间通信可以拆分为两个词: 组件 通信 回顾Vue系列的文章,组件是vue中最强大的功能之一,同样组件化是React的核心思想 相比vue,React的组件更加灵活和多样,按照不同 ...

  10. .NET中常见的加解密方式

    在互联网普及的初期,人们更关注单纯的连接性,以不受任何限制地建立互联网为最终目的.正如事情都具有两面性,互联网的便捷性给人们带来了负面问题,计算机病毒的侵害.信息泄露.网络欺诈等利用互联网的犯罪行为日 ...

随机推荐

  1. Grafana 系列-统一展示-2-Prometheus 数据源

    系列文章 Grafana 系列文章 Grafana Prometheus 数据源 Grafana 提供了对 Prometheus 的内置支持.本文会介绍 Grafana Prometheus(也包括 ...

  2. springMVC中注解

    其中常见注解: 1.@RequestMapping:用于请求url路径,可用于类和方法上,用于类上,则表示类中所有响应请求的方法都是以该路径作为父路径. 2.@RequestBody:注解实现接收 h ...

  3. 2023-04-05:做甜点需要购买配料,目前共有n种基料和m种配料可供选购。 制作甜点需要遵循以下几条规则: 必须选择1种基料;可以添加0种、1种或多种配料,每种类型的配料最多添加2份, 给定长度为

    2023-04-05:做甜点需要购买配料,目前共有n种基料和m种配料可供选购. 制作甜点需要遵循以下几条规则: 必须选择1种基料:可以添加0种.1种或多种配料,每种类型的配料最多添加2份, 给定长度为 ...

  4. 2020-09-25:rust中Point是结构体类型,【let p1=Point{x:25,y:25};let p2=p1;】请问p1和p2是同一个对象吗?

    福哥答案2020-09-25:#福大大架构师每日一题# 不是同一个对象.p1和p2直接存内容,不存地址.这点跟java里的new对象是不一样的.p1.x和p1的地址相同,说明p1存的是内容,而不是地址 ...

  5. Selenium - 基础知识介绍

    Selenium - 基础知识介绍 介绍 Selenium是ThoughtWorks员工在业余时间开发并维护的开源项目,并且在ThoughtWorks的项 目中被广泛应用. 简单地说,Selenium ...

  6. python---序列化小结

    python 序列化 1 什么叫序列化 在我们存储数据或网络传输数据时候,需要多我们对象进行处理,把对象处理成方便储存和网络传输的数据格式,这个过程叫做序列化 2 对象序列化有三种方式; 2.1 pi ...

  7. windows内核学习一

    变量类型 kernel user ULONG unsigned long PULONG unsigned long* UCHAR unsigned char PUCHAR unsigned char* ...

  8. python利用subprocess执行shell命令

    subprocess以及常用的封装函数 运行python的时候,我们都是在创建并运行一个进程.像Linux进程那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序.在Python中 ...

  9. 韩顺平Spring体系化笔记(内含ioc,aop,动态代理等底层原理)

    Spring Spring 核心学习内容 IOC.AOP. JdbcTemplate.声明式事务 1.Spring 几个重要概念 Spring 可以整合其他的框架(Spring 是管理框架的框架) S ...

  10. 春秋杯春季联赛&&ciscn2023华北赛区部分题解

    前言 复现几个比赛时没做出来的题 1.[CISCN 2023 华北赛区]ez_ruby 查文档可知 ruby内置的open函数,如果第一个字符是管道符|,后面就可以接命令.这可能是考察涉猎的知识范围广 ...