丑话说在前面
强烈建议至少刷一遍 《官方文档》,反复研读 《Hooks FAQ》
这里主要以本人关注点聚合,方便理解用于实践
如下是上一代标准写法类组件的缺点,也正是hook要解决的问题css
设计目的html
Function Component中每次Render都会造成一个快照并保留下来,这样就确保了状态可控,hook默认每次都更新,会致使重复请求等一系列问题,若是给[]就会一尘不变,所以用好hooks最重要就是学会控制它的变化react
在大部分状况下咱们只要遵循 React 的默认行为,由于 React 只更新改变了的 DOM 节点,不太重新渲染仍然花费了一些时间,除非它已经慢到让人注意了git
基于上面的两点,咱们一般的解决方案是:github
传统上认为,在 React 中使用内联函数对性能的影响,与每次渲染都传递新的回调会如何破坏子组件的 shouldComponentUpdate 优化有关, 使用useCallback缓存函数引用,再传递给通过优化的并使用引用相等性去避免非必要渲染的子组件时,它将很是有用编程
const Button = React.memo((props) => { // 你的组件 }, fn);// 也能够自定义比较函数
function Table(props) { // ⚠️ createRows() 每次渲染都会被调用 const [rows, setRows] = useState(createRows(props.count)); // ... // ✅ createRows() 只会被调用一次 const [rows, setRows] = useState(() => createRows(props.count)); // ... }
function Image(props) { // ⚠️ IntersectionObserver 在每次渲染都会被建立 const ref = useRef(new IntersectionObserver(onIntersect)); // ... } function Image(props) { const ref = useRef(null); // ✅ IntersectionObserver 只会被惰性建立一次 function getObserver() { if (ref.current === null) { ref.current = new IntersectionObserver(onIntersect); } return ref.current; } // 当你须要时,调用 getObserver() // ... }
useEffect(function persistForm() { if (name !== '') { localStorage.setItem('formData', name); } });
useEffect(() => { document.title = "Hello, " + name; }, [name]); // 以useEffect为示例,适用于全部hook
直到 name 改变时的 Rerender,useEffect 才会再次执行,保证了性能且状态可控segmentfault
useEffect(() => { const id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, [count]);// 以useEffect为示例,适用于全部hook
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
实际应用场景每每不是一个hook能搞定的,长篇大论未必说的清楚,直接上例子(来源于官网摘抄,网络收集,自我总结)数组
【将更新与动做解耦】-【useEffect,useReducer,useState】浏览器
该函数将接收先前的 state,并返回一个更新后的值缓存
useEffect(() => { const id = setInterval(() => { setCount(c => c + 1); }, 1000); return () => clearInterval(id); }, []);
import React, { useReducer, useEffect } from "react"; const initialState = { count: 0, step: 1, }; function reducer(state, action) { const { count, step } = state; if (action.type === 'tick') { return { count: count + step, step }; } else if (action.type === 'step') { return { count, step: action.step }; } else { throw new Error(); } } export default function Counter() { const [state, dispatch] = useReducer(reducer, initialState); const { count, step } = state; console.log(count); useEffect(() => { const id = setInterval(() => { dispatch({ type: 'tick' }); }, 1000); return () => clearInterval(id); }, [dispatch]); return ( <> <h1>{count}</h1> <input value={step} onChange={e => { dispatch({ type: 'step', step: Number(e.target.value) }); }} /> </> ); }
【经过 context 往下传一个 dispatch 函数】-【createContext,useReducer,useContext】
/**index.js**/ import React, { useReducer } from "react"; import Count from './Count' export const StoreDispatch = React.createContext(null); const initialState = { count: 0, step: 1, }; function reducer(state, action) { const { count, step } = state; switch (action.type) { case 'tick': return { count: count + step, step }; case 'step': return { count, step: action.step }; default: throw new Error(); } } export default function Counter() { // 提示:`dispatch` 不会在从新渲染之间变化 const [state, dispatch] = useReducer(reducer, initialState); return ( <StoreDispatch.Provider value={dispatch}> <Count state={state} /> </StoreDispatch.Provider> ); } /**Count.js**/ import React, { useEffect,useContext } from 'react'; import {StoreDispatch} from '../index' import styles from './index.css'; export default function(props) { const { count, step } = props.state; const dispatch = useContext(StoreDispatch); useEffect(() => { const id = setInterval(() => { dispatch({ type: 'tick' }); }, 1000); return () => clearInterval(id); }, [dispatch]); return ( <div className={styles.normal}> <h1>{count}</h1> <input value={step} onChange={e => { dispatch({ type: 'step', step: Number(e.target.value) }); }} /> </div> ); }
【层层依赖,各自管理】-【useEffect,useCallback,useContext】
function App() { const [count, setCount] = useState(1); const countRef = useRef();// 在组件生命周期内保持惟一实例,可穿透闭包传值 useEffect(() => { countRef.current = count; // 将 count 写入到 ref }); // 只有countRef变化时,才会从新建立函数 const callback = useCallback(() => { const currentCount = countRef.current //保持最新的值 console.log(currentCount); }, [countRef]); return ( <Parent callback={callback} count={count}/> ) } function Parent({ count, callback }) { // count变化才会从新渲染 const child1 = useMemo(() => <Child1 count={count} />, [count]); // callback变化才会从新渲染,count变化不会 Rerender const child2 = useMemo(() => <Child2 callback={callback} />, [callback]); return ( <> {child1} {child2} </> ) }
function usePrevious(value) { const ref = useRef(); useEffect(() => { ref.current = value; }); return ref.current; }
function useUpdate(fn) { const mounting = useRef(true); useEffect(() => { if (mounting.current) { mounting.current = false; } else { fn(); } }); }
function useIsMounted(fn) { const [isMount, setIsMount] = useState(false); useEffect(() => { if (!isMount) { setIsMount(true); } return () => setIsMount(false); }, []); return isMount; }
function useInertRef(obj) { // 传入一个实例 new IntersectionObserver(onIntersect) const ref = useRef(null); if (ref.current === null) { // ✅ IntersectionObserver 只会被惰性建立一次 ref.current = obj; } return ref.current; }
参考文章