react|react hooks使用总结

前言
很早就知道hooks,但是那个时候并没有去使用,就是大概的了解了下,到后来使用了hooks,但是用的不深,总是对一些依赖不知道怎么去处理,也只会用useState,所以经过几篇文章的总结,对hooks有了一个更深的了解,做个记录方便以后查阅
传统的组件
我们使用最多的就是class组件,这种类型的组件我觉得使用还是很有必要的,因为在写class组件的时候,你会了解es6的class相关知识,以及this的指向问题。但是由于class组件的过度语法糖话,导致越来越多的人更倾向于函数组件,但是函数组件最开始用法很单一,只是作为一个ui组件(无状态组件),为了在函数组件里面使用状态,hooks就应运而生了
useState
如果把useState做比喻的话,相当于class组件中的state和setState,他能够让我们定义状态,并且去改变状态,setState参数可以是一个函数,函数的参数是上一个状态值,我们看一下代码就能明白他的作用

import React, { useState } from "react"; const FuncText = () => { const [count, setCount] = useState(0); return ( setCount(count + 1)}>我是count,自动加1:{count} ); }; export default FuncText;

解释 count 相当于class组件的this.state.count,setCount相当于setState({count: count + 1}),其中count默认值是0
注意点 上面这段代码其实还是很好理解,但是有一个点需要注意,就是useState里面如果是一个引用类型的值,比如数组,对象的时候。如果不生成一个新的引用,他是不对触发更新,因为他觉得值没有改变,如下
import React, { useState } from "react"; const FuncText = () => { console.log("第二次我不会触发"); const [count, setCount] = useState({ name: "json" }); //const [count, setCount] = useState({ count: [0] }); return ( { count.name = "xiaoming"; setCount(count); // 不会触发 setCount({ ...count}); // 会触发 }} > 我是count,自动加1:{JSON.stringify(count)}); }; export default FuncText;

useCllback
useCallback接受一个函数,返回一个函数,他也可以有依赖,如果依赖变了就会返回一个新函数,如果没有变,就会一直是旧函数,那么useCllback在哪里可以被用到了,我们有时候会遇到,把一个函数当作依赖,但是这个函数可能不需要改变,(那么为什么还要把他当依赖了,因为我们可能要复用这个函数,就会把这个函数提到外面,一旦提出去的话lint检查规则,会认为他是一个依赖)
但是一旦你把函数当作依赖,每次渲染函数会变化,那么这个组件就会出现死循环了,因为每次组件改变,函数都是新的,那么如果确保函数一直不变或者根据依赖值去变化,就是useCllback的用途看下面的代码
unction SearchResults() { // ? Preserves identity when its own deps are the same const getFetchUrl = useCallback((query) => { return 'https://hn.algolia.com/api/v1/search?query=' + query; }, []); // ? Callback deps are OKuseEffect(() => { const url = getFetchUrl('react'); // ... Fetch data and do something ... }, [getFetchUrl]); // ? Effect deps are OKuseEffect(() => { const url = getFetchUrl('redux'); // ... Fetch data and do something ... }, [getFetchUrl]); // ? Effect deps are OK// ... }

useReducer
useReducer和react中的redux一套流程比较相似,就是传入reducer和initState,然后给你返回state值和dispatch,那么为什么要用他了,他主要也是解决依赖问题,有时候我们的依赖值会一直发生变化,比如在定时器里面改变state的值,那么useEffect会执行多次,但是我们就只是想让值变化,怎么办了这个时候useReducer就有用了,看代码
const initState = { sum: 0 }; const reducer = (state, action) => { const { sum } = state; switch (action.type) { case "sumOne": return { sum: sum + 1 }; default: console.log("none"); } }; const [state, dispatch] = useReducer(reducer, initState); const { sum } = state; useEffect(() => { const timer = setInterval(() => { // setSum(sum => sum + 1); dispatch({ type: "sumOne" }); }, 1000); return () => { clearInterval(timer); }; }, []);

dispatch之后返回的state重新给到sum,所以值被改变
解释 这段代码摘自hooks文档上的,我们看到getFetchUrl 为了复用成了依赖,但是如果不用useCllback会死循环,所以用useCllback包裹起来,这样这个函数永远但是不变的(依赖是空),所以就达到了效果,那么如果想要随着query变化的话,就把query放入依赖数组即可
useEffect
useEffect应该是hooks里面最难也是最需要理解的一个点,useEffect第一个参数是需要处理的函数,第二个参数有几种状态,下面分别讲解
注意点1参数问题 参数什么都不传 当useEffect第二个参数什么都不传的时候,就相当于class组件的componentDidMount和componentDidUpdate,每次更新都会执行
参数为空数组([]) 当参数为空数组的时候,只会执行一个,相当于componentDidMount
参数为有值得数组的时候[count] 有值得话相当于你传入了依赖,只有在依赖改变的时候才会去执行,当然第一次加载也会,相当于componentDidMount和componentWillReceiveProps,具体我们看一下下面的代码
import React, { useState, useEffect } from "react"; const FuncText = () => { const [count, setCount] = useState(0); useEffect(() => { console.log("我是依赖count的打印"); }, [count]); useEffect(() => { console.log("我是空数组的打印"); }, []); useEffect(() => { console.log("我是什么都不依赖的打印"); }); return ( setCount(count + 1)}>我是count, 自动加1: {count} ); }; export default FuncText;

react|react hooks使用总结
文章图片
image.png useEffect注意点2取消绑定问题 useEffect的取消绑定是在useEffect第一个函数里面返回一个函数,如果依赖是一个空数组,那么组件卸载的时候就会去执行取消绑定的操作,但是如果依赖没有,那么组件会在每一个渲染都会执行取消绑定函数,如果是一个有值得依赖,那么就会在每一次依赖值改变的时候,也会去执行取消绑定的操作
import React, { useState, useEffect } from "react"; const Text = props => { const [sum, setSum] = useState(0); useEffect(() => { const timer = setInterval(() => { setSum(sum + 1); }, 1000); return () => { console.log("每一次sum改变都会执行"); clearInterval(timer); }; }, [sum]); return ( { console.log(sum, "count---"); setSum(sum + 1); }} > 我还能改变自己}); }; export default Text;

useEffect注意点3取值问题 useEffect里面的props和state的值,都是取得是当次渲染时候的值,注意不是最新的那次,因为useEffect用了闭包的原理,存储了每一个渲染时候props和state的值,所以每次useEffect都会拿到那个时候的值,而不会随着时间的变化去最新的,这里有一hooks文档上提供的例子https://codesandbox.io/s/lyx20m1ol
可以看一下,这里就不写例子了
useEffect注意点4依赖问题 useEffect我觉得最难的是依赖问题,如何去找依赖,如果不接触lint规则的话,最开始写这个的话,很难找出依赖,幸好我们有eslint-plugin-react-hooks 插件的exhaustive-depslint规则,可以帮助我们在写useEffect的时候,为我们找出依赖,如果不借助的话,我的思路是如果useEffect里面使用的变量,在useEffect里面没有定义,都可以看作需要依赖,那么正确找出依赖有什么好处了,看下面的代码
import React, { useState, useEffect } from "react"; const Text = props => { const [sum, setSum] = useState(0); useEffect(() => { const timer = setInterval(() => { setSum(sum + 1); }, 1000); return () => { console.log("每一次sum改变都会执行"); clearInterval(timer); }; }, []); return ( { console.log(sum, "count---"); setSum(sum + 1); }} > 我还能改变自己}); }; export default Text;

react|react hooks使用总结
文章图片
image.png
代码给出警告说缺少sum依赖,一旦不写这个依赖,导致的问题是sum永远都是1,因为他只会执行一次,拿到的是初始化值0,但是如果加上依赖的话,我们每一个改变sum都会取消绑定和重新绑定的操作,代码优化不够,那么如何解决这个问题了有两种方式
方式1 还记得setSum的函数写法么,函数的参数就可以拿到之前的状态,所以我们可以这么写
useEffect(() => { console.log("执行一次"); const timer = setInterval(() => { setSum(sum => sum + 1); }, 1000); return () => { clearInterval(timer); }; }, []);

【react|react hooks使用总结】useEffect只执行一次,但是sum每次都会正确改变
方式2 使用useReducer,上面已经讲解到了,看上面代码即可
useRef
useRef一般有两个作用一个是获取类组件的实例,一个是定义一个全局的变量,因为设置了useRef之后再去重新渲染他的值不会改变,看下面的代码
用法1
import React, { useState, useEffect, useRef } from "react"; import Text from "./Text"; const FuncText = () => { const [count, setCount] = useState(0); const textRef = useRef(""); useEffect(() => { console.log("我是依赖count的打印"); }, [count]); useEffect(() => { console.log("我是空数组的打印"); }, []); useEffect(() => { console.log("我是什么都不依赖的打印"); }); return ( { console.log(textRef.current); // 获取到了text的值 setCount(count + 1); }} > 我是count, 自动加1: {count} ); }; export default FuncText;

用法2 充当一个全局变量,还记得我们之前说的么,useEffect每次都是取到的是当次渲染的值,那么能不能做到像class那样拿到最新的了,这个就需要用到ref了,看下面的代码
useContext
useContext就相对比较简单一点就是获取context的值,减少props的传递,看代码如下
import React, { useContext } from "react"; import context from "../util/context"; const ContextText = () => { const { count } = useContext(context); return 我是context的count:{count}; }; export default ContextText; // context.js import React, { createContext } from "react"; const context = createContext(0); export default context;

useMemo
useMemo就是hooks里面的一个优化手段,每次改变state的值得话,函数组件被重新渲染,那么如果有很多无关紧要的函数就会执行,用useMemo就会避免这个问题,如下代码
const { count } = props; const [sum, setSum] = useState(0); const add = useMemo(() => { return sum + 1; }, [sum]);

只有在cum改变时候才去执行useMemo代码,不然不去执行

    推荐阅读