react hooks基本使用

前言

在hooks没有出现之前,函数组件基本只做展示功能,涉及到状态管理,我们需要用到class组件或者数据管理框架(redux/dva/mobx);

小编福利推荐,更多精彩内容请点击链接,点击这里

hooks组件趋向函数化编程。更简单,组件颗粒度更小,代码也更少。class组件有如下不足:

问题 解决 缺点
生命周期复杂,逻辑耦合
大型组件逻辑难以复用,难以拆分 继承解决 不能多继承
class组件有this指针

而 hooks 对这些问题都有较好的解决方案

  • 通过自定义hooks解决多继承
  • 通过自定义 useEffect 来解决复用问题,细分逻辑,减小出现逻辑臃肿的场景

为了解决这种,类组件功能齐全却很重,纯函数也有重大限制。也就有了React Hooks,也就是加强版的函数组件,我们可以完全不使用 class,就能写出一个全功能的组件。

hooks常用API讲解

useState

hooks 的能力,就是让我们在函数组件中使用 state, 就是通过 useState 来实现的。

useState 会返回一个数组,第一个值是我们的 state, 第二个值是一个函数,用来修改该 state 的。

请看下边Demo 在线运行:

import React, { useState } from "react";
import "./styles.css";

export default function App() {
  const [count, setCount] = useState(0);

  const addOne = () => {
    setCount(count + 1);
  };

  const addTwo = () => {
    setCount((pre) => pre + 2);
    console.log(count);
  };

  return (
    
点击次数: {count}

); }

useState(defaultValue) defaultValue可以一个值,也可以是一个函数,返回一个值。
也可以setState((previewState => { return newState }))这样来更新状态

useEffect

Effect Hook 可以让你在函数组件中执行副作用操作,什么是副作用呢,就是除了状态相关的逻辑,比如网络请求,监听事件,查找 dom

useEffect支持两个参数,第一个参数是函数

第二个参数,有三种情况

  • 什么都不传,组件每次 render 之后 useEffect 都会调用,相当于 componentDidMount 和 componentDidUpdate
  • 传入一个空数组 [], 只会调用一次,相当于 componentDidMount 和 componentWillUnmount
  • 传入一个数组,其中包括变量,只有这些变量变动时,useEffect 才会执行

useEffect 实现生命周期

useEffect可以看做 componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个声明周期函数的组合。

请看下边Demo 在线运行:

import React, { useState, useEffect } from "react";
import "./styles.css";

function Child() {
  const [count, setCount] = useState(0);
  const [width, setWidth] = useState(document.body.clientWidth);

  const onChange = () => {
    setWidth(document.body.clientWidth);
  };

  useEffect(() => {
    // 相当于 componentDidMount
    console.log("componentDidMount");
    window.addEventListener("resize", onChange, false);

    return () => {
      // 相当于 componentWillUnmount
      console.log("componentWillUnmount");
      window.removeEventListener("resize", onChange, false);
    };
  }, []);

  useEffect(() => {
    // 相当于 componentDidUpdate
    console.log("componentDidUpdate");
    document.title = count;
  });

  useEffect(() => {
    console.log(`count change: count is ${count}`);
  }, [count]);

  return (
    
页面名称: {count}
页面宽度: {width}
); } function App() { const [visible, setVisible] = useState(true); return (
{visible && }
); } export default App;

useContext

context 实例中的 ProviderConsumer,在类组件和函数组件中都能使用,contextType 只能在类组件中使用,因为它是类的静态属性,具体如何使用 useContext

请看下边Demo 在线运行:

import React, { useState, useContext, Component, createContext } from "react";
import "./styles.css";

// 创建一个 context
const Context = createContext(0);

// 组件一, Consumer 写法
class Item1 extends Component {
  render() {
    return {(count) => 
{count}
}
; } } // 组件二, contextType 写法 class Item2 extends Component { static contextType = Context; render() { const count = this.context; return
{count}
; } } // 组件一, useContext 写法 function Item3() { const count = useContext(Context); return
{count}
; } function App() { const [count, setCount] = useState(0); return (
点击次数: {count}
); } export default App;

三种写法都能够实现我们的需求,但是,三种写有各自的优缺点,下面为对比出的结果

写法 优缺
consumer 嵌套复杂,Consumer 第一个子节点必须为一个函数,无形增加了工作量
contextType 只支持 类组件,无法在多 context 的情况下使用
useContext 不需要嵌套,多 context 写法简单

结论:useContext写法简单,功能丰富

useMemo

useMomo一版是用来做优化使用的,可以和Vue的计算属性做对比

useMemo 也支持传入第二个参数,用法和 useEffect 类似

  1. 不传数组,每次UI更新结束都会重新计算

  2. 空数组,只会计算一次

  3. 依赖对应的值,当对应的值发生变化时,才会重新计算(可以依赖另外一个 useMemo 返回的值)

    请看下边Demo 在线运行:

import { useMemo, useState, useCallback, memo } from "react";
import "./styles.css";

const Child = memo(({ value }) => {
  console.log("child 渲染", value);
  return 
{value}
; }); const Child1 = memo(({ value, add }) => { console.log("child1 渲染", value); return (
{value}
); }); export default function App() { const [n, setN] = useState(0); const [m, setM] = useState(0); const add = useMemo(() => { return () => { setM(m + 1); } }, [m]); const n1 = useMemo(() => { return n + 1; }, [n]); return (
n+1={n1}
{/* */}
); }

上边使用useMemo返回一个add方法,当其它状态值发生变化时,add方法的指针不会发生变化,也就不会引起Child1组件不必要的更新。如果这里不适用useMemo监听m值依赖变化返回一个方法,而直接使用如下

const add = () => {
    setM(m+1);
}

这样当其它状态发生变化的时候,组件重新渲染,函数重新执行,add方法指针发生变化,Child1组件也会更新。所以当我们向子组件传入一个方法的时候尽量使用useMemo的形式。

useCallBack

useCallback 是 useMemo 的语法糖。在 react 中我们经常面临一个子组件渲染优化的问题, 尤其是在向子组件传递函数props时,props包含函数时,每次 render 都会创建新函数,导致子组件不必要的渲染,浪费性能。这个时候,就需要使用是 useCallback ,useCallback 可以保证,当依赖值没有变化是,无论 render 多少次,我们的函数都是同一个函数,减小不断创建的开销。

请看下边Demo 在线运行:

import { useMemo, useState, useCallback, memo } from "react";
import "./styles.css";

const Child = memo(({ value }) => {
  console.log("child 渲染", value);
  return 
{value}
; }); const Child1 = memo(({ value, add }) => { console.log("child1 渲染", value); return (
{value}
); }); export default function App() { const [n, setN] = useState(0); const [m, setM] = useState(0); const add = useCallback(() => { setM(m + 1); }, [m]); const n1 = useMemo(() => { return n + 1; }, [n]); return (
n+1={n1}
{/* */}
); }

参数同useMemo

useRef

有两点使用

  1. 获取组件的实例(class组件)

  2. 在函数组件中的一个全局变量,不会因为重复 render 重复申明, 类似于类组件的 this.xxx,(也可以理解UI不依赖的数据)

    请看下边Demo 在线运行:

import React, {
  useMemo,
  useState,
  useRef,
  PureComponent,
  forwardRef
} from "react";
import "./styles.css";

const Children1 = forwardRef((props) => {
  const { count } = props;
  return 
{count}
; }); class Children extends PureComponent { render() { const { count } = this.props; return
{count}
; } } function App() { const [count, setCount] = useState(0); const childrenRef = useRef(null); const children1Ref = useRef(null); const inputRef = useRef(null); const onClick = useMemo(() => { return () => { console.log("button click"); console.log(childrenRef.current.props); console.log(children1Ref.current); setCount((count) => count + 1); }; }, []); return (
点击次数: {count}
{ console.log(inputRef.current.value); }} />
); } export default App;

useImperativeHandle

useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值,也就是子组件可以选择性的暴露给父组件一些方法,这样可以隐藏一些私有方法和属性,官方建议,useImperativeHandle应当与 forwardRef 一起使用

请看下边Demo 在线运行:

import React, {
  useCallback,
  useState,
  useRef,
  useImperativeHandle,
  forwardRef
} from "react";
import "./styles.css";

const Child = forwardRef((props, ref) => {
  const introduce = useCallback(() => {
    console.log("子组件 introduce方法");
  }, []);

  useImperativeHandle(ref, () => ({
    introduce: () => {
      introduce();
    }
  }));

  return 
子组件{props.count}
; }); function App() { const [count, setCount] = useState(0); const childRef = useRef(null); const child1Ref = useRef(null); const onClick = useCallback(() => { setCount((count) => count + 1); console.log(childRef.current); console.log(child1Ref.current); childRef.current.introduce(); }, []); return (
点击次数: {count}
); } export default App;

自定义hook

自定义hooks可以将组件逻辑复用,颗粒度更小。也是用来解决class组件逻辑难以复用的问题

自定义hooks规则:

  • use开头
  • 函数内部可以使用hooks提供的api

请看下边Demo 在线运行:

import React, { useEffect, useState, useCallback } from "react";
import "./styles.css";

function useMousePosition() {
  const [position, setPosition] = useState({
    x: 0,
    y: 0
  });

  const onMouseMove = useCallback((event) => {
    const { clientX, clientY } = event;
    setPosition({
      x: clientX,
      y: clientY
    });
  }, []);

  useEffect(() => {
    window.addEventListener("mousemove", onMouseMove, false);
    return () => {
      window.removeEventListener("mousemove", onMouseMove, false);
    };
  }, []);

  return position;
}

function useDomSize() {
  const [size, setSize] = useState({
    width: document.documentElement.clientWidth,
    height: document.documentElement.clientHeight
  });

  const onResize = useCallback(() => {
    setSize({
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight
    });
  }, []);

  useEffect(() => {
    window.addEventListener("resize", onResize, false);
    return () => {
      window.removeEventListener("resize", onResize, false);
    };
  }, []);

  return size;
}

export default function App() {
  const size = useDomSize();
  const position = useMousePosition();
  return (
    

宽:{size.width}, 高:{size.height}

x:{position.x}, y:{position.y}

); }

你可能感兴趣的:(react hooks基本使用)