Hooks见证不一样的函数组件

React Hooks

什么是Hooks

Hooks是一个新的react特性提案,适用于函数式组件(视图组件),如果需要外部react特性(比如状态管理、生命周期),就用钩子把外部特性“钩”进来,典型标志是函数名字都是以use开头。

hooks出现的背景

  • 跨组件复用stateful logic十分困难 使用Hooks,你可以在将含有state的逻辑从组件中抽象出来,这将可以让这些逻辑容易被测试。同时,Hooks可以帮助你在不重写组件结构的情况下复用这些逻辑。
  • 复杂的组件难以理解 Hooks允许您根据相关部分(例如设置订阅或获取数据)将一个组件分割成更小的函数,而不是强制基于生命周期方法进行分割
  • 不止是用户,机器也对Classes难以理解 Hooks让你可以在classes之外使用更多React的新特性

最常用钩子函数

useState(),useEffect(),useRef()

hooks钩子函数介绍

  1. useState
    格式为const [XXX, setXXX] = useState(initValue),数组第一个值为变量名,第二个值为更新变量值的函数,initValue为变量初始值。
let [count, setCount] = useState(100);
  1. useEffect
    在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子),发送ajax请求数据、设置订阅/启动定时器、手动操作真实dom。其作用相当于类组件中以下三个生命周期函数:componentDidMount()、componentDidUpdate()、componentWillUnmount()
useEffect(() => {
   	// 在此可执行任何副作用操作
   	return () => {
   		// 该函数在组件卸载前执行,可在此做一些收尾工作,比如清楚定时器,取消订阅
   	}
}, [stateValue]) // 如果第二个参数是[ ],回调函数只会在第一次render()后执行,填写的是需要监听改变的数据

react执行顺序流程图

  1. useLayoutEffect
  • useEffect 在全部渲染完毕后才会执行
  • useLayoutEffect 会在 浏览器 layout 之后,painting 之前执行
  • 其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect
  • 可以使用它来读取 DOM 布局并同步触发重渲染
  • 在浏览器执行绘制之前 useLayoutEffect 内部的更新计划将被同步刷新
  • 会阻塞页面的渲染,如果在里面执行耗时任务的话,页面就会卡顿
  1. useContext 直接获取祖先元素通过createContext创建的context;
  2. useReducer
const [state, dispatch] = useReducer(reducer, initialState, init);
  1. memo,useMemo和useCallback在优化组件的应用场景
  • 在子组件不需要父组件值和函数的情况下,使用memo包裹即可
  • 如果是函数作为props,可以使用useCallback保证不会反复修改
  • 如果是值作为props,可以使用useMemo保证值不会反复修改
  1. useRef
    不符合capture values,本身不会变化,存储的.current会变化
    会触发值的变化,不会调起render的重新渲染,多用于逻辑层数据处理,涉及到页面重新渲染的需要使用useState,简单理解,用于逻辑处理需要同步更新的值需要用useRef。
  2. useImperativeHandle
    配合forwardRef使用,用于自定义通过ref给父组件暴露的值
  3. useDebugValue
    用于开发者工具调试

封装自定义Hooks

封装hooks获取窗口大小

// useWinSize
import React, { useState ,useEffect ,useCallback } from 'react';

export default function useWinSize(){
    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)
        return ()=>{
            window.removeEventListener('resize',onResize)
        }
    },[])
 
    return size;
}

// demo.js
import useWinSize from './useWinSize'

export default function(){
    const size = useWinSize()
    return (
        <div>页面Size:{size.width}x{size.height}</div>
    )
}

延迟设置值

import React, {useState, useRef, useEffect} from 'react'

export const useDelayState = (initialState)=>{
    const [state, setState] = useState(initialState);
    const ref = useRef();
    const delaySetState = (value, delay)=>{
        ref.current = setTimeout(()=>{
            setState(value)
        }, delay)
    }
    useEffect(()=>{
        return ()=>{
            clearTimeout(ref.current)
        }
    }, [])
    return [state, delaySetState, setState]
}

GeoLocation获取地理定位

import { useEffect, useState } from 'react';

const useGeolocation = options => {
  const [state, setState] = useState({
    loading: true,
    accuracy: null,
    altitude: null,
    altitudeAccuracy: null,
    heading: null,
    latitude: null,
    longitude: null,
    speed: null,
    timestamp: Date.now(),
  });
  let mounted = true;
  let watchId: any;

  const onEvent = event => {
    if (mounted) {
      setState({
        loading: false,
        accuracy: event.coords.accuracy,
        altitude: event.coords.altitude,
        altitudeAccuracy: event.coords.altitudeAccuracy,
        heading: event.coords.heading,
        latitude: event.coords.latitude,
        longitude: event.coords.longitude,
        speed: event.coords.speed,
        timestamp: event.timestamp,
      });
    }
  };
  const onEventError = (error) =>
    mounted && setState(oldState => ({ ...oldState, loading: false, error }));

  useEffect(() => {
    navigator.geolocation.getCurrentPosition(onEvent, onEventError, options);
    watchId = navigator.geolocation.watchPosition(onEvent, onEventError, options);

    return () => {
      mounted = false;
      navigator.geolocation.clearWatch(watchId);
    };
  }, []);

  return state;
};
export default useGeolocation;

Hooks的使用注意事项

  • 只能在顶层调用Hooks? Hooks是使用数组或单链表串联起来,Hooks顺序修改会打乱执行结果
  • useState在多个组件中引入,彼此之间会不会有影响? 在React中Hooks把数据存在fiber node上的,每个组件都有自己的currentlyRenderingFiber.memoizedState

Hooks的问题

  • Hooks能解决组件状态的复用问题,但没有很好的解决JSX复用问题
  • React Hooks模糊了生命周期的概念,但也带来了更高门槛的学习(Hooks生命周期的理解、Hooks Rules的理解、useEffect依赖项的判断等)
  • 类拥有比函数更丰富的表达能力(OOP),Function Component容易使代码逻辑混乱

Hooks的原理

  • 单向链表通过next把hooks串联起来
  • memoizedState存在fiber node上,组件之间不会相互影响
  • useState和useReducer中通过dispatchAction调度更新任务
  • 15
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值