「学习笔记」ReactHooks入门

📖前言

本文是本人在学习ReactHooks记录的学习笔记,内容不只限于文档中的内容,涉及了Hooks源码相关。若是有错误,还请及时指正。javascript

❓什么是Hook?为何须要Hook?

推荐看一下Dan Abramov的博客html

image

  1. 逻辑复用,若是使用高阶组件等特性,较为复杂。
  2. 传统的函数组件没法存储state状态。
  3. Hooks容许在函数组件中,调用React的功能。
  4. 使用自定义Hook能够简化逻辑复用。
  5. Hooks是React的将来
  6. Hooks不是什么魔法。Hooks的设计也与React无关。

⛰️useState

useState能够在函数组件中,添加state Hook。java

调用useState会返回一个state变量,以及更新state变量的方法。useState的参数是state变量的初始值,初始值仅在初次渲染时有效react

更新state变量的方法,并不会像this.setState同样,合并state。而是替换state变量。ajax

下面是一个简单的例子, 会在页面上渲染count的值,点击setCount的按钮会更新count的值。spring

image

函数式更新

若是新的state须要以前的state计算获得。能够向useState返回的更新函数中,传递一个函数。函数的参数是前一个state。api

image

惰性的初始值

useState的初始值是惰性的,只会在初次渲染组件的时候起做用。若是state的初始值需用经过复杂计算获得,useState的初始值也能够是一个函数,函数的返回值将是useState的初始值。数组

image

跳过state更新

调用state的更新函数,传入和当前同样的state时。React会跳过子组件的渲染,以及effect的执行。浏览器

image

小节

  1. useState的更新是替换,而不是合并。
  2. useState能够接收函数参数,并将函数的返回值做为初始值
  3. useState的更新函数能够接收函数做为参数,函数的参数是前一状态的state值。
  4. 使用当前的值,对state进行更新不会触发渲染。
  5. 可使用多个useState,声明多个state变量。

🏁useEffect

除了官方文档外,推荐阅读一下Um guia completo para useEffect性能优化

useEffect可让咱们在函数组件中执行反作用操做。事件绑定,数据请求,动态修改DOM。

useEffect将会在每一次React渲染以后执行。不管是初次挂载时,仍是更新。(固然这种行为咱们能够控制)

image

须要清除的effect

在传统的class组件中,一般在componentDidMount中添加对事件的监听。在componentWillUnmount中会清除对事件的监听。 咱们须要在不一样的生命周期函数中,拆分咱们的逻辑。

而effect能够返回一个函数,当react进行清除时, 会执行这个返回的函数。每当执行本次的effect时,都会对上一个effect进行清除。组件卸载时也会执行进行清除。

也就是说,下面的代码中。每一次更新,都会对上一次的effect进行卸载,并执行本次的effect。

image

effect性能优化

每次执行effect,清除上一次effect可能会形成没必要要的性能浪费。咱们能够经过effect的第二个参数,控制effect的执行。 第二个参数是useEffect的依赖,只有当依赖发生变化时,useEffect才会更新。

image

当咱们传递传递一个空数组做为依赖时,会告诉React,effect不依赖任何state或者props。咱们可使用此行为模拟componentDidMount或者componentWillUnmount。

⚠️ 请记住使用空数组的effect和componentDidMount是有差别的

image

🤔️useEffect(fn, [])和componentDidMount的差别有什么差别?

相似的问题,为何有时候在effect里拿到的是旧的state或prop呢?

image

useEffect会捕获props, state,可是始终是初始的值。 如上图所示,当咱们点击button屡次,effect在3秒以后的回调,打印的依然是state的初始值。

为何会这样?

这是由于javascript闭包的机制,函数组件被调用后,函数内部的state因为被内部定时器的回调所依赖,因此没有被垃圾回收机制所清除,而是保存在内存里了。因此等定时器的回掉执行时,打印的是以前闭包中存储的变量。

如何避免拿到旧的prop或者state?

  1. 使用useRef
  2. 查看是否遗漏了依赖, 致使effect没有更新

🤔️如何正确的useEffect中请求数据?

阅读这篇文章是一个不错的选择How to fetch data with React Hooks?

如何请求数据?

image

如何在useEffect中使用async/await?

async函数默认会返回一个Promise对象,而useEffect中,只容许什么都不返回或者返回一个清除函数。控制台中,会触发以下警告 **Warning: useEffect function must return a cleanup function or nothing. Promises and useEffect(async () => …) are not supported, but you can call an async function inside an effect.. **

解决方案以下👇

image

小节

  1. 可使用多个useEffect分离逻辑。在class组件中,咱们一般会在componentDidMount中,混杂添加事件绑定,请求数据等多种无关的逻辑。
  2. 若是只须要useEffect在加载时执行一次,或者卸载组件时执行一次,能够向useEffect传递一个空数组。
  3. useEffect不支持直接使用async函数
  4. 若是在useEffect中造成了闭包,将会拿到旧的props,或者state。(这个与Hook无关,与函数组件自己的特性相关)
  5. useEffect返回的函数,清除函数。会在上一个effect被清除时调用。

📰Hook规则

  1. 只在最顶层使用Hook,不在判断,循环语句中使用Hook
  2. 只在React函数中使用Hook

🤔️为何Hook高度依赖执行顺序?

咱们借助preact的源码进行分析,在preact中Hook的存储在组件的私有属性__hooks._list的数组中。读取和存储都依赖currentIndex的指针,若是hook的执行顺序改变,currentIndex也会被改变,获取的hook多是完成错误的。

image

⚙️自定义Hook

  1. 自定义Hook必须以use开头
  2. 自定义Hook只是逻辑复用,其中的state是不会共享的。
  3. 自定义Hook内部能够调用其余Hook。
  4. 避免过早的拆分抽象逻辑

下面是一个自定义Hook的🌰

💡自定义数据获取Hook

咱们经过ajax请求表哥数据时,不少逻辑都是通用的。好比loading的状态的处理,错误信息的处理,翻页的处理。咱们能够把这些逻辑抽象成一个公共的Hook。不一样的api,做为自定义Hook的参数。

下面是一个数据请求自定义Hook的例子:

image

如何使用?从自定义Hook向外暴露一些state,和setState。当page发生改变时,会从新请求数据。

image

🚀其余API

useContext

接收一个context对象,并返回当前的context的值。useContext能够订阅context的变化。可是仍然须要上层组件使用<MyContext.Provider>来为下层组件提供context。

image

useReducer

useReducer接收三个参数,reducer函数,initialArg初始值,init惰性初始值函数。reducer函数和Redux的reducer相似,接收state,以及action。返回更新后的state。若是传入三个参数,init(initialArg)将做为初始值。

useReducer在复杂场景下比useState更适用。

指定初始state

useReducer的第二个参数能够指定初始的state

惰性初始化

若是指定useReducer的第三个参数,useReducer的初始值会被设置为init(initialArg)

image

跳过dispatch

若是ReducerHook的返回的state与当前state相同,React将跳过子组件的渲染及effect的执行。

🌰例子

在自定义Hook的例子中,使用了多个useState进行状态管理,当出现大量状态时,useState会使得逻辑变得很复杂。咱们如今可使用useReducer管理多个state,也可向子组件传递dispatch,而不是回调函数。

下面是将自定义Hook中,请求数据自定义Hook的例子,改形成useReducer的方法。

image

useCallback

useCallback接收回调函数和依赖数组做为参数。useCallback会返回memoized函数。当依赖项改变的时候,会返回的新的memoized函数。

image

useMemo

useMemo和useCallback相似。useMemo会返回memoized值。当依赖项改变时,会从新计算memoized值。

image

🌟useRef

useRef除了获取dom节点的功能外,useRef的current属性,能够方便保存任何可变值。useRef每一次渲染时,都会返回同一个ref对象。

设想下面👇这种状况, 组件从新渲染时,因为timer发生了变化,咱们会永远没法清除定时器。

image

若是想要在更新后,依然能够清除定时器,能够将timer,保存到useRef的current属性上

image

useLayoutEffect

image
(红圈中是同步的操做)

useLayoutEffect和useEffect相似,可是不一样的是:

  • useEffect,使用useEffect不会阻塞浏览器的重绘
  • useLayoutEffect, 使用useLayoutEffect,会阻塞浏览器的重绘。若是你须要手动的修改Dom,推荐使用useLayoutEffect。由于若是在useEffect中更新Dom,useEffect不会阻塞重绘,用户可能会看到由于更新致使的闪烁,

image

🚗参考

相关文章
相关标签/搜索