React hooks使用简介

195 阅读4分钟

react hooks作用

   之前的react组件主要有两种方式实现:Class方式和function方式。一般无状态组件可以使用function方式写,大部分组件还是使用Class方式写的。

两种方式存在的问题:

  • Class组件:1.组件复杂时,很难拆分;2.组件拆分颗粒度较细时,基础组件上很难加东西;3.多个组件间的重复代码很难复用。
  • function组件:只能渲染无状态组件。    那么使用Hooks解决了啥问题呢?
  1. 可以使用function方式+hooks来编写有状态的组件;
  2. 相同逻辑的代码可以剥离出来,解决了代码复用问题;
  3. 组件拆分更容易

常用的react hooks

useState

    useState是用来声明状态变量,例子如下:

    import { useState } from 'react';
    const [count, setCount] = useState(0);

   useState这个函数接受的参数是状态的初始值,返回一个数组,数组的第0位是当前的状态值,第1位是可以改变状态值的方法函数。上面例子中使用ES6的解构方式获取数据。

   初始状态可以是数字、字符串、对象、数组,甚至是方法(如果是方法的话,执行该方法,返回值作为初始状态)。

   注意:React Hooks不能在条件判断语句、循环函数中使用,因为它必须有完全一样的渲染顺序。

useEffect

    useEffect支持副作用(如异步请求,手动操作Dom,定时任务等),用来代替Class组件中的生命周期函数——componentDidMount(首次渲染)和componentDidUpdate(更新state导致的渲染)。

    import { useEffect } from 'react';
    useEffect(()=>{
        console.log('useEffect')
    }, []);

    useEffect的第二个参数是可选的,用来优化useEffect,只有参数发生变化时,才能执行副作用函数。     解绑副作用是如何实现的呢?

useEffect(()=>{
        console.log('useEffect')
        return ()=>{
            console.log('useEffect off')
        }
},[])

    上面的例子就实现了componentDidMount+componentwillUnMount的生命周期,重点是第二个参数传了空数组。

useContext

    实现跨层级传值,让父子组件传值更简单。

    父组件:


import { createContext } from 'react';
const CountContext = createContext();

function Example(){
    const [ count , setCount ] = useState(0);

    return (
        <div>
            <CountContext.Provider value={count}>
                <Counter/>
            </CountContext.Provider>
        </div>
    )
}
export default Example;

    子组件:

import React, { useContext } from 'react';

function Counter(){
    const count = useContext(CountContext)  //一句话就可以得到count
    return (<h2>{count}</h2>)
}

useReducer

    和redux很相近,在state复杂的时候使用,使代码具有更好的可读性和可维护性。

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

    注意:使用 useState 获取的 setState 方法更新数据时是异步的;而使用 useReducer 获取的 dispatch 方法更新数据是同步的。  

useCallback和useMemo

主要用于React hooks的性能优化。   例子如下:

import React , { useMemo, useCallback} from 'react';

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]);

    useCallback接受函数和一个数组输入,并返回的一个缓存版本的回调函数,仅当重新渲染时数组中的值发生改变时,才会返回新的函数实例。

    useMemo与useCallback类似, 返回的是一个缓存的值。仅当重新渲染时数组中的值发生改变时,回调函数才会重新计算缓存数据,这可以避免在每次重新渲染时都进行复杂的数据计算。

    唯一的区别是:useCallback 不会执行第一个参数函数,而是将它返回给你,而 useMemo 会执行第一个函数并且将函数执行结果返回给你。   

useRef 和 useImperativeHandle

    useRef用来获取DOM元素,保存变量。 例子如下:

const refContainer = useRef(initialValue);

    useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

    useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。 useImperativeHandle 应当与 forwardRef 一起使用:

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);

useLayoutEffect

    useLayoutEffect与useEffect具有相同的api,都是执行副作用操作。

    useLayoutEffect的用途:当你的useEffect里面有操作DOM元素,并改变页面样式时,就需要用到useLayoutEffect,否则就可能会出现闪屏的问题。

    产生闪屏原因是:useEffect是异步的,其异步就是利用requestIdleCallback,在浏览器空闲时间执行传入的callback;而useLayoutEffect是同步的,useLayoutEffect里面的callback函数会在DOM更新完以后立即执行,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制。大部分情况下,用哪个都一样,但是如果副作用执行时间过长,如存在大量计算,useLayoutEffect就会造成渲染阻塞,所以官方推荐尽量使用useEffect。