react hooks 在底层依赖单向链表,同时在组件对应的fiber 对象的 memoizedState 属性上保存该FunctionComponent对应的Hooks链表

  1. // App组件对应的fiber对象
  2. const fiber = {
  3. // 保存该FunctionComponent对应的Hooks链表
  4. memoizedState: null,
  5. // 指向App函数
  6. stateNode: App
  7. };

通过workInProgressHook变量指向当前正在工作的hook。

  1. workInProgressHook = fiber.memoizedState;

在组件render时,每当遇到下一个useState,我们移动workInProgressHook的指针。

  1. 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是同步的。

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和业务能够分开维护
登陆场景:

  1. const initState = {
  2. name: '',
  3. pwd: '',
  4. isLoading: false,
  5. error: '',
  6. isLoggedIn: false,
  7. };
  8. function loginReducer(state: any, action: any) {
  9. switch (action.type) {
  10. case 'login':
  11. return {
  12. ...state,
  13. isLoading: true,
  14. error: '',
  15. };
  16. case 'success':
  17. return {
  18. ...state,
  19. isLoggedIn: true,
  20. isLoading: false,
  21. };
  22. case 'error':
  23. return {
  24. ...state,
  25. error: action.payload.error,
  26. };
  27. }
  28. }
  29. function LoginPage() {
  30. const [state, dispatch] = useReducer(loginReducer, initState);
  31. const { name, pwd, isLoading, error, isLoggedIn } = state;
  32. const login = (e) =>{
  33. e.preventDefault();
  34. dispatch(type: 'login')
  35. login({name, pwd}).then(() =>{
  36. dispatch({type: 'success'});
  37. })
  38. .catch((error) =>{
  39. dispatch({
  40. type: 'error',
  41. payload: {error: error.message}
  42. })
  43. })
  44. }
  45. return (
  46. // 返回JSX页面
  47. )
  48. }

useContext

useContext
,useContext肯定与React.createContext有关系的,接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 的 value prop 决定。

为什么使用
如果你在接触 Hook 前已经对 context API 比较熟悉,那应该可以理解,useContext(MyContext) 相当于 class 组件中的 static contextType = MyContext 或者 。简单点说就是useContext是用来消费context API的

如何使用
const value = useContext(MyContext);
知识点合集
useContext造成React.memo 无效
当组件上层最近的 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。即使祖先使用 React.memo 或 shouldComponentUpdate,❗️也会在组件本身使用 useContext 时重新渲染。
[

](https://blog.csdn.net/qq_34998786/article/details/111677540)

  1. // 创建一个 context
  2. const Context = React.createContext()
  3. // 用memo包裹
  4. const Item = React.memo((props) => {
  5. // 组件一, useContext 写法
  6. const count = useContext(Context);
  7. console.log('props', props)
  8. return (
  9. <div>{count}</div>
  10. )
  11. })
  12. const App = () => {
  13. const [count, setCount] = useState(0)
  14. return (
  15. <div>
  16. 点击次数: { count}
  17. <button onClick={() => { setCount(count + 1) }}>点我</button>
  18. <Context.Provider value={count}>
  19. <Item />
  20. </Context.Provider>
  21. </div>
  22. )
  23. }

自定义hooks

https://juejin.cn/post/7022777747722207269
自定义hooks和普通函数没什么区别。
自定义Hooks很简单,利用官方提供的Hook我们可以把重用的业务逻辑抽离出来,也就是我们的自定义Hook,当你在一个项目中发现大量类似代码,那就抽离成Hooks吧。遵循单一职责原则

  1. function usePageChange2() {
  2. useEffect(() => {
  3. const token = Cookies.get('token');
  4. // 如果token不存在就跳转到登陆页面
  5. if (!token) {
  6. history.push(loginPath);
  7. }
  8. }, [location.pathname, location.pathname?.search]);
  9. }
  10. // 获取浏览器窗口尺寸
  11. function useWinSize() {
  12. const [size, setSize] = useState({
  13. width: document.documentElement.clientWidth,
  14. height: document.documentElement.clientHeight,
  15. });
  16. const onResize = useCallback(() => {
  17. setSize({
  18. width: document.documentElement.clientWidth,
  19. height: document.documentElement.clientHeight,
  20. });
  21. }, []);
  22. useEffect(() => {
  23. window.addEventListener('resize', onResize);
  24. return () => {
  25. window.removeEventListener('resize', onResize);
  26. };
  27. }, []);
  28. }
  1. import { useState, useEffect, useCallback } from 'react';
  2. import { request } from '@ipalfish/bisheng-stone-compatible';
  3. export default function usePageList(site) {
  4. const [list, setList] = useState([]);
  5. const refreshPageList = useCallback(() => {
  6. request({
  7. // url: '/paltrackapi/base/paltrack/identifier/list', // 换接口
  8. url: '/paltrackapi/base/paltrack/spmB_identifier/list',
  9. params: {
  10. spmA_identifier_id: site,
  11. limit: 0,
  12. offset: 0,
  13. },
  14. successMessage: false,
  15. converter: (ctx) => {
  16. const { data: items } = ctx.data || {};
  17. return items?.map((item, index) => {
  18. // const { description, spm_name, id } = item;
  19. // return { label: description + '(' + spm_name + ')', value: id };
  20. const { cname, spmB_identifier_id, ...rest } = item;
  21. return {
  22. label: cname,
  23. value: spmB_identifier_id,
  24. ...rest,
  25. };
  26. });
  27. },
  28. }).then((rs = []) => {
  29. setList(rs);
  30. });
  31. }, [setList, site]);
  32. useEffect(() => {
  33. refreshPageList();
  34. }, [refreshPageList]);
  35. return [list, refreshPageList, { setList, push, unshift }];
  36. }
  37. // 使用
  38. const [pageList = [], refreshPageList, { unshift }] = usePageList(site);

useRequest

https://juejin.cn/post/6844904064388431880
https://github.com/umijs/umi-request