React Hooks
什么是Hooks
Hooks是一个新的react特性提案,适用于函数式组件(视图组件),如果需要外部react特性(比如状态管理、生命周期),就用钩子把外部特性“钩”进来,典型标志是函数名字都是以use开头。
hooks出现的背景
- 跨组件复用stateful logic十分困难 使用Hooks,你可以在将含有state的逻辑从组件中抽象出来,这将可以让这些逻辑容易被测试。同时,Hooks可以帮助你在不重写组件结构的情况下复用这些逻辑。
- 复杂的组件难以理解 Hooks允许您根据相关部分(例如设置订阅或获取数据)将一个组件分割成更小的函数,而不是强制基于生命周期方法进行分割
- 不止是用户,机器也对Classes难以理解 Hooks让你可以在classes之外使用更多React的新特性
常用hooks
- useState
let [count, setCount] = useState(100);
- useEffect
useEffect(()=>{
return ()=>{ }
}, [])
- useLayoutEffect
- useEffect 在全部渲染完毕后才会执行
- useLayoutEffect 会在 浏览器 layout 之后,painting 之前执行
- 其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect
- 可以使用它来读取 DOM 布局并同步触发重渲染
- 在浏览器执行绘制之前 useLayoutEffect 内部的更新计划将被同步刷新
- 会阻塞页面的渲染,如果在里面执行耗时任务的话,页面就会卡顿
- useContext 直接获取祖先元素通过createContext创建的context;
- useReducer
const [state, dispatch] = useReducer(reducer, initialState, init);
- memo,useMemo和useCallback在优化组件的应用场景
- 在子组件不需要父组件值和函数的情况下,使用memo包裹即可
- 如果是函数作为props,可以使用useCallback保证不会反复修改
- 如果是值作为props,可以使用useMemo保证值不会反复修改
- useRef
不符合capture values,本身不会变化,存储的.current会变化 - useImperativeHandle
配合forwardRef使用,用于自定义通过ref给父组件暴露的值 - 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调度更新任务
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block
var foo = 'bar';