react hooks 在底层依赖单向链表,同时在组件对应的fiber 对象的 memoizedState 属性上保存该FunctionComponent对应的Hooks链表
// App组件对应的fiber对象const fiber = {// 保存该FunctionComponent对应的Hooks链表memoizedState: null,// 指向App函数stateNode: App};
通过workInProgressHook变量指向当前正在工作的hook。
workInProgressHook = fiber.memoizedState;
在组件render时,每当遇到下一个useState,我们移动workInProgressHook的指针。
workInProgressHook = workInProgressHook.next;
这样,只要每次组件render时useState的调用顺序及数量保持一致,那么始终可以通过workInProgressHook找到当前useState对应的hook对象。
不同类型hook的memoizedState保存不同类型数据,具体如下:
- useState:对于const [state, updateState] = useState(initialState),memoizedState保存state的值
 - useReducer:对于const [state, dispatch] = useReducer(reducer, {});,memoizedState保存state的值
 - useEffect:memoizedState保存包含useEffect回调函数、依赖项等的链表数据结构effect,你可以在这里(opens new window)看到effect的创建过程。effect链表同时会保存在fiber.updateQueue中
 - useRef:对于useRef(1),memoizedState保存{current: 1},useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
 
一个常见的用例便是命令式地访问子组件
- useMemo:对于useMemo(callback, [depA]),memoizedState保存[callback(), depA]
 - useCallback:对于useCallback(callback, [depA]),memoizedState保存[callback, depA]。与useMemo的区别是,useCallback保存的是callback函数本身,而useMemo保存的是callback函数的执行结果
 
有些hook是没有memoizedState的,比如:
- useContext
 
useLayoutEffect和useEffect 的区别
useLayoutEffect和useEffect一样也是处理副作用,其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
❗️官方尽量推荐使用useEffect,因为useLayoutEffect,useLayoutEffect里面的callback函数会在DOM更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制
区别就是:useEffect是异步的,useLayoutEffect是同步的。
useMemo 和 useCallback 的区别
都会在组件第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行;
useMemo返回缓存的变量,useCallback返回缓存的函数。
useReducer
作为useState 的替代方案。它接收一个形如(state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)
为什么使用
官方说法: 在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。
总结来说:
如果你的state是一个数组或者对象等复杂数据结构
如果你的state变化很复杂,经常一个操作需要修改很多state
如果你希望构建自动化测试用例来保证程序的稳定性
如果你需要在深层子组件里面去修改一些状态(也就是useReducer+useContext代替Redux)
如果你用应用程序比较大,希望UI和业务能够分开维护
登陆场景:
const initState = {name: '',pwd: '',isLoading: false,error: '',isLoggedIn: false,};function loginReducer(state: any, action: any) {switch (action.type) {case 'login':return {...state,isLoading: true,error: '',};case 'success':return {...state,isLoggedIn: true,isLoading: false,};case 'error':return {...state,error: action.payload.error,};}}function LoginPage() {const [state, dispatch] = useReducer(loginReducer, initState);const { name, pwd, isLoading, error, isLoggedIn } = state;const login = (e) =>{e.preventDefault();dispatch(type: 'login')login({name, pwd}).then(() =>{dispatch({type: 'success'});}).catch((error) =>{dispatch({type: 'error',payload: {error: error.message}})})}return (// 返回JSX页面)}
useContext
useContext
,useContext肯定与React.createContext有关系的,接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 
为什么使用
如果你在接触 Hook 前已经对 context API 比较熟悉,那应该可以理解,useContext(MyContext) 相当于 class 组件中的 static contextType = MyContext 或者 
如何使用
const value = useContext(MyContext);
知识点合集
useContext造成React.memo 无效
当组件上层最近的 
[
](https://blog.csdn.net/qq_34998786/article/details/111677540)
// 创建一个 contextconst Context = React.createContext()// 用memo包裹const Item = React.memo((props) => {// 组件一, useContext 写法const count = useContext(Context);console.log('props', props)return (<div>{count}</div>)})const App = () => {const [count, setCount] = useState(0)return (<div>点击次数: { count}<button onClick={() => { setCount(count + 1) }}>点我</button><Context.Provider value={count}><Item /></Context.Provider></div>)}
自定义hooks
https://juejin.cn/post/7022777747722207269
自定义hooks和普通函数没什么区别。
自定义Hooks很简单,利用官方提供的Hook我们可以把重用的业务逻辑抽离出来,也就是我们的自定义Hook,当你在一个项目中发现大量类似代码,那就抽离成Hooks吧。遵循单一职责原则
function usePageChange2() {useEffect(() => {const token = Cookies.get('token');// 如果token不存在就跳转到登陆页面if (!token) {history.push(loginPath);}}, [location.pathname, location.pathname?.search]);}// 获取浏览器窗口尺寸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);};}, []);}
import { useState, useEffect, useCallback } from 'react';import { request } from '@ipalfish/bisheng-stone-compatible';export default function usePageList(site) {const [list, setList] = useState([]);const refreshPageList = useCallback(() => {request({// url: '/paltrackapi/base/paltrack/identifier/list', // 换接口url: '/paltrackapi/base/paltrack/spmB_identifier/list',params: {spmA_identifier_id: site,limit: 0,offset: 0,},successMessage: false,converter: (ctx) => {const { data: items } = ctx.data || {};return items?.map((item, index) => {// const { description, spm_name, id } = item;// return { label: description + '(' + spm_name + ')', value: id };const { cname, spmB_identifier_id, ...rest } = item;return {label: cname,value: spmB_identifier_id,...rest,};});},}).then((rs = []) => {setList(rs);});}, [setList, site]);useEffect(() => {refreshPageList();}, [refreshPageList]);return [list, refreshPageList, { setList, push, unshift }];}// 使用const [pageList = [], refreshPageList, { unshift }] = usePageList(site);
useRequest
https://juejin.cn/post/6844904064388431880
https://github.com/umijs/umi-request
