本文是本人在学习ReactHooks记录的学习笔记,内容不只限于文档中的内容,涉及了Hooks源码相关。若是有错误,还请及时指正。javascript
useState能够在函数组件中,添加state Hook。java
调用useState会返回一个state变量,以及更新state变量的方法。useState的参数是state变量的初始值,初始值仅在初次渲染时有效。react
更新state变量的方法,并不会像this.setState同样,合并state。而是替换state变量。ajax
下面是一个简单的例子, 会在页面上渲染count的值,点击setCount的按钮会更新count的值。spring
若是新的state须要以前的state计算获得。能够向useState返回的更新函数中,传递一个函数。函数的参数是前一个state。api
useState的初始值是惰性的,只会在初次渲染组件的时候起做用。若是state的初始值需用经过复杂计算获得,useState的初始值也能够是一个函数,函数的返回值将是useState的初始值。数组
调用state的更新函数,传入和当前同样的state时。React会跳过子组件的渲染,以及effect的执行。浏览器
除了官方文档外,推荐阅读一下Um guia completo para useEffect性能优化
useEffect可让咱们在函数组件中执行反作用操做。事件绑定,数据请求,动态修改DOM。
useEffect将会在每一次React渲染以后执行。不管是初次挂载时,仍是更新。(固然这种行为咱们能够控制)
在传统的class组件中,一般在componentDidMount中添加对事件的监听。在componentWillUnmount中会清除对事件的监听。 咱们须要在不一样的生命周期函数中,拆分咱们的逻辑。
而effect能够返回一个函数,当react进行清除时, 会执行这个返回的函数。每当执行本次的effect时,都会对上一个effect进行清除。组件卸载时也会执行进行清除。
也就是说,下面的代码中。每一次更新,都会对上一次的effect进行卸载,并执行本次的effect。
每次执行effect,清除上一次effect可能会形成没必要要的性能浪费。咱们能够经过effect的第二个参数,控制effect的执行。 第二个参数是useEffect的依赖,只有当依赖发生变化时,useEffect才会更新。
当咱们传递传递一个空数组做为依赖时,会告诉React,effect不依赖任何state或者props。咱们可使用此行为模拟componentDidMount或者componentWillUnmount。
⚠️ 请记住使用空数组的effect和componentDidMount是有差别的
相似的问题,为何有时候在effect里拿到的是旧的state或prop呢?
useEffect会捕获props, state,可是始终是初始的值。 如上图所示,当咱们点击button屡次,effect在3秒以后的回调,打印的依然是state的初始值。
这是由于javascript闭包的机制,函数组件被调用后,函数内部的state因为被内部定时器的回调所依赖,因此没有被垃圾回收机制所清除,而是保存在内存里了。因此等定时器的回掉执行时,打印的是以前闭包中存储的变量。
阅读这篇文章是一个不错的选择How to fetch data with React Hooks?
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.. **
解决方案以下👇
咱们借助preact的源码进行分析,在preact中Hook的存储在组件的私有属性__hooks._list的数组中。读取和存储都依赖currentIndex的指针,若是hook的执行顺序改变,currentIndex也会被改变,获取的hook多是完成错误的。
下面是一个自定义Hook的🌰
咱们经过ajax请求表哥数据时,不少逻辑都是通用的。好比loading的状态的处理,错误信息的处理,翻页的处理。咱们能够把这些逻辑抽象成一个公共的Hook。不一样的api,做为自定义Hook的参数。
下面是一个数据请求自定义Hook的例子:
如何使用?从自定义Hook向外暴露一些state,和setState。当page发生改变时,会从新请求数据。
接收一个context对象,并返回当前的context的值。useContext能够订阅context的变化。可是仍然须要上层组件使用<MyContext.Provider>来为下层组件提供context。
useReducer接收三个参数,reducer函数,initialArg初始值,init惰性初始值函数。reducer函数和Redux的reducer相似,接收state,以及action。返回更新后的state。若是传入三个参数,init(initialArg)将做为初始值。
useReducer在复杂场景下比useState更适用。
useReducer的第二个参数能够指定初始的state
若是指定useReducer的第三个参数,useReducer的初始值会被设置为init(initialArg)
若是ReducerHook的返回的state与当前state相同,React将跳过子组件的渲染及effect的执行。
在自定义Hook的例子中,使用了多个useState进行状态管理,当出现大量状态时,useState会使得逻辑变得很复杂。咱们如今可使用useReducer管理多个state,也可向子组件传递dispatch,而不是回调函数。
下面是将自定义Hook中,请求数据自定义Hook的例子,改形成useReducer的方法。
useCallback接收回调函数和依赖数组做为参数。useCallback会返回memoized函数。当依赖项改变的时候,会返回的新的memoized函数。
useMemo和useCallback相似。useMemo会返回memoized值。当依赖项改变时,会从新计算memoized值。
useRef除了获取dom节点的功能外,useRef的current属性,能够方便保存任何可变值。useRef每一次渲染时,都会返回同一个ref对象。
设想下面👇这种状况, 组件从新渲染时,因为timer发生了变化,咱们会永远没法清除定时器。
若是想要在更新后,依然能够清除定时器,能够将timer,保存到useRef的current属性上
useLayoutEffect和useEffect相似,可是不一样的是: