一文带你了解React Hooks

简介

Hook 是 React 16.8 的新增特性,它让你在不写 类组件 class component 时也能使用 state 以及其他的 React 特性。

1.为什么要使用Hooks

(1)组件之间复用状态逻辑很难

(2)复杂组件变的难以理解

(3)难以理解的class

为了解决这些问题,Hook使你在非class的情况下可以使用更多的React特性。从概念上讲,React组件一直更像是函数。而Hook则拥抱了函数,同时也没有牺牲React的精神原则。Hook提供了问题的解决方案,无需学习复杂的函数式或响应式编程技术。

2.React中的Hook

2.1 UseState — 用于定义组件的state,对标类组件中的this.state功能

const [state, setState] = useState(initialState);

const [状态,修改该状态的方法] = useState(初始值);
复制代码

在同一个组件中可以 使用useState定义多个状态

useState返回的setState方法,不会进行对象合并

useState返回的setState方法同样是异步方法,会被批处理所监管

function App(props){
    const [count,setCount] = useState(0);
    const [nub,setNub] = useState(10);
    return <div>
        <p>count:{count}</p>
        <button onClick={()=>{
            setCount(count+1);
        }}>递增</button>
        <p>nub:{nub}</p>
        <button onClick={()=>{
            setNub(nub+1);
        }}>递增</button>
    </div>
}
复制代码

hooks 使用时注意事项

1. hooks 只能在 react 函数中使用(函数式组件,自定义 hook)
2. hooks 在使用必须保证其调用顺序,不能将hooks的调用,放在 if for 等流程控制语句中,也不能将 hooks 调用放在子函数中。hooks 只能放在 函数组件(或自定义 hook)的最外层调用(必须保证其调用顺序: 就是每次执行该函数式,hooks的调用顺序,不会改变)
复制代码

为什么不要在循环、条件或嵌套函数中调用Hook?

Hooks的设计是基于数组实现。在调用时按顺序加入数组中,如果使用循环、条件、嵌套函数很有可能导致数组取值错位,执行错误的Hook.实质上React的源码里并不是数组而是链表t
复制代码

为什么useState返回的是数组而不是对象?

如果useState返回数组,那么你可以顺便对数组中的变量命名,代码看起来也比较干净.而如果是对象的话返回的值必须和useState内部实现返回的对象同名,这样你只能在function component中使用一次,想要多次使用useState必须得重命名返回值
复制代码
2.2 useEffect 副作用(DOM操作,数据请求) hook – 主要用来处理组件中的作用类型,用于替代生命周期
    useEffect(() => {
        effect: 副作用函数
        return () => {
            cleanup 清理函数
        }
    }, [input]) input: 依赖参数
复制代码
  挂载阶段: 从上到下执行函数组件,如果碰到 useEffect 就将其中的 effect 存储到一个队列中,当组件挂载完成之后,按照队列顺序执行 effect 函数,
    并获取 cleanup 函数 存贮至一个新的队列

    更新阶段: 从上到下执行函数组件,如果碰到 useEffect 就将其中的 effect 存储到一个队列中,当组件更新完成之后,会将之前存储的 cleanup 函数队列
    按照顺序执行, 然后 执行 effect 队列,并获取 cleanup 函数 存贮至一个新的队列。在更新阶段会观察依赖参数的值有没有发生变化,如果没有变化就
    不执行对应的 cleanup 和 effect

    卸载阶段: 找到之前存储的 cleanup 函数队列,依次执行


    依赖参数:
        1. null 组件每次更新都执行
        2. [] 组件更新时不执行
        3. [依赖参数,依赖参数2……] 只要有一个依赖参数有变化就执行
复制代码
2.3 useLayoutEffect

useLayoutEffect主要在useEffect函数结果不满意时才被用到,一般的经验是当处理dom改变带来的副作用才会被用到,该hook执行时,浏览器并未对dom进行渲染,较useEffect执行要早

useEffect和useLayoutEffect的差异?

1.useEffect和useLayoutEffect在Mount和Update阶段执行方法一样,传参不一样
2.useEffect异步执行,而useLayoutEffect同步执行
3.Effect用HookPassive标记useEffect,用HookLayout标记useLayoutEEffect
复制代码
2.4 useRef 类似于createRef
   除了可以保存实例之外,还可以保存组件更新前的一些数据
   当 ref 存储的是数据,数据不会随着组件的更新,而自动更新
复制代码
function InputFocus() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}
复制代码
2.5 useImperativeHandle
useImperativeHandle(ref, createHandle, [deps])
复制代码

useImperativeHandle可以让你在使用ref时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用ref这样的命令式代码。useImperativeHandle应当与fowardRed一起使用

1.ref:定义 current 对象的 ref createHandle:一个函数,返回值是一个对象,即这个 ref 的 current
2.对象 [deps]:即依赖列表,当监听的依赖发生变化,useImperativeHandle 才会重新将子组件的实例属性输出到父组件
3.ref 的 current 属性上,如果为空数组,则不会重新输出。
复制代码
import { useRef, useImperativeHandle, forwardRef } from "react";

function FancyInput(props, ref) {
  useImperativeHandle(ref, () => ({
    save: () => {
      console.log("保存");
    },
  }));
  return <input />;
}

FancyInput = forwardRef(FancyInput);

function App() {
  const inputRef = useRef(null);
  return (
    <>
      <span onClick={() => inputRef.current.save()}>保存</span>
      <FancyInput ref={inputRef} />
    </>
  );
}

export default App;
复制代码
2.6 useMemo

当依赖参数,有变化时,执行相应函数,并返回函数的返回值

import { useState, useMemo } from "react";

function App() {
  const [count, setCount] = useState(1);
  const applePrice = useMemo(() => {
    return count * 38;
  }, [count]);
  return (
    <div>
      <p>
        count:{count} 斤车厘子,单价:38元,总价{applePrice}元
      </p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        count-递增
      </button>
    </div>
  );
}

export default App;
复制代码

2.7 useCallback
返回一个函数,当监听的数据发生变化才会执行回调函数

import { useState, useCallback } from "react";

function App() {
  const [count1, setCount1] = useState(1);
  const [count2, setCount2] = useState(2);
  const [count3, setCount3] = useState(3);
  const handleBtn1 = () => {
    setCount1(count1 + 1);
  }
  const handleBtn2 = useCallback(() => {
    setCount2(count2 + 2);
  }, [count2]);
  return (
    <div>
      <h3>{count1}</h3>
      <button
        onClick={handleBtn1}
      >
        count1
      </button>
      <h3>{count2}</h3>
      <button onClick={handleBtn2}>count2</button>
      <h3>{count3}</h3>
      <button
        onClick={() => {
          setCount3(count3 + 3);
        }}
      >
        count3
      </button>
    </div>
  );
}

export default App;

复制代码
2.8 useContext 作用就是对它所包含的组件树提供全局共享数据的一种技术

首先创建context.js

import { createContext } from "react";
export const MyContext = createContext({});
复制代码

父组件

import React, { useState } from "react";
import Counter from "./count";
import { MyContext } from "./context";

const App = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>父组件点击数量:{count}</p>
      <button onClick={() => setCount(count + 1)}>{"点击+1"}</button>
      <MyContext.Provider value={count}>
        <Counter />
      </MyContext.Provider>
    </div>
  );
};

export default App;
复制代码

子组件

import React, { useContext } from "react";
import { MyContext } from "./context";

function Counter(props) {
  const count = useContext(MyContext);

  return (
    <>
      <p>子组件获得的点击数量:{count}</p>
    </>
  );
}

export default Counter;

复制代码
2.9.自定义Hooks

下面举个简单的例子:使用自定义Hooks获取state或props上一次的值
自定义Hooks获取state或props上一次的值

function usePreVal(val) {
  const preValue = useRef();
  useEffect(() => {
    preValue.current = val;
  }, [val]);
  return preValue.current;
}
复制代码

自定义Hooks的使用

import { useState, useRef, useEffect } from "react";
function App() {
  const [count, setCount] = useState(1);
  const [num, setNum] = useState(5);
  const preCount = usePreVal(count);
  const preNum = usePreVal(num);
  return (
    <>
      <div>
        最新的{count}之前的{preCount}
      </div>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        count递增
      </button>
      <div>
        最新的{num}之前的{preNum}
      </div>
      <button
        onClick={() => {
          setNum(num + 5);
        }}
      >
        num递增
      </button>
    </>
  );
}
export default App;
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享