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 (
    <div onClick={() => setCount(count + 1)}>我是count,自动加1:{count}</div>
  );
};

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 (
    <div
      onClick={() => {
        count.name = "xiaoming";
        setCount(count); // 不会触发
        setCount({ ...count}); // 会触发
      }}
    >
      我是count,自动加1:{JSON.stringify(count)}
    </div>
  );
};

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 OK

  useEffect(() => {
    const url = getFetchUrl('react');
    // ... Fetch data and do something ...
  }, [getFetchUrl]); // ✅ Effect deps are OK

  useEffect(() => {
    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 (
    <div onClick={() => setCount(count + 1)}>我是count, 自动加1: {count}</div>
  );
};

export default FuncText;
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 (
    <span
      onClick={() => {
        console.log(sum, "count---");
        setSum(sum + 1);
      }}
    >
      我还能改变自己}
    </span>
  );
};

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 (
    <span
      onClick={() => {
        console.log(sum, "count---");
        setSum(sum + 1);
      }}
    >
      我还能改变自己}
    </span>
  );
};

export default Text;
image.png

代码给出警告说缺少sum依赖,一旦不写这个依赖,导致的问题是sum永远都是1,因为他只会执行一次,拿到的是初始化值0,但是如果加上依赖的话,我们每一个改变sum都会取消绑定和重新绑定的操作,代码优化不够,那么如何解决这个问题了有两种方式

方式1

还记得setSum的函数写法么,函数的参数就可以拿到之前的状态,所以我们可以这么写

 useEffect(() => {
    console.log("执行一次");
    const timer = setInterval(() => {
      setSum(sum => sum + 1);
    }, 1000);

    return () => {
      clearInterval(timer);
    };
  }, []);

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 (
    <div
      onClick={() => {
        console.log(textRef.current); // 获取到了text的值
        setCount(count + 1);
      }}
    >
      我是count, 自动加1: {count}
      <Text ref={textRef} />
    </div>
  );
};

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 <div>我是context的count:{count}</div>;
};

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代码,不然不去执行

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容