前端指南 前端指南
指南
资源
  • 刷力扣 (opens new window)
  • 手写题 (opens new window)
  • 归档
  • 分类
  • 标签
  • 关于我
  • 关于本站
GitHub (opens new window)

Seognil LC

略懂点前端
指南
资源
  • 刷力扣 (opens new window)
  • 手写题 (opens new window)
  • 归档
  • 分类
  • 标签
  • 关于我
  • 关于本站
GitHub (opens new window)
  • React Hooks 学习指南

    • Hooks 简介
      • 什么是 React Hooks
      • 为什么要用 Hooks
    • 学习 Hooks
      • 概览
      • 学习路线
    • 资料
      • 我的学习代码
      • 自学教材
      • 实战
      • 进阶
      • 扩展阅读
    • Hooks 知识体系
      • Hooks 主要概念
    • Hooks 典型代码
      • Hooks 大致用法
    • Hooks 相关
      • 使用上的注意点
      • Hooks 大致原理
      • Hooks 和 Class 的差异
      • 第二参数和优化
      • Hooks 的运用
  • note
  • frontend
  • react
Seognil LC
2019-11-17
目录

React Hooks 学习指南

React Hooks 学习指南

# Hooks 简介

# 什么是 React Hooks

Hooks 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

换句话说:只用函数 + Hooks 来完成组件编写。

Hooks 隐藏了 Class 写法中生命周期的概念,
或者说 React 自己尽量帮你处理了生命周期。

# 为什么要用 Hooks

如果你喜欢写 FP 而不是 OOP 风格的代码,
那么你可能早就开始用函数来写 Presentational Components 了。

但是函数组件有自己的局限性,
而 Hooks 突破了函数组件的局限性,
实现了一些 Class 组件才有的功能。

# 学习 Hooks

# 概览

  • 耗时:从入门到熟悉需要大约 10~20 小时(个人估计)
  • 难点:
    • 理解 FP 范式(并知道 Hooks 并不是纯 FP)
    • 理解 JS 执行原理
    • 探索最佳实践
  • 工具:
    • react@16.8+ (opens new window)
    • webpack/parcel/cli

# 学习路线

  • 前置学习
    • 理解 functional programming
      • side effect
    • JavaScript 进阶指南
  • 了解 React 基本概念
    • 理解 展示组件、容器组件 的区别
    • 学习 Context 的特性
  • 学习 Hooks
    • 练习所有 React 自带的基本 Hooks API
    • 结合 TypeScript
    • 了解下 react-use(社区 Hooks)
    • 对比 Hooks 和 Class 写法的异同
  • 实战
    • 敲点小型应用 Demo
    • 结合 redux/rxjs
  • 进阶
    • 编写自己的 custom hooks
    • 阅读 Hooks 相关源码
  • 迷思
    • useEffect 的作用和运用
    • 探索最佳实践

# 资料

# 我的学习代码

  • React - Learn By Doing (opens new window)

# 自学教材

  • Hooks API 索引 (opens new window)
  • How to useContext in React? - RWieruch (opens new window)
  • 【译】什么时候使用 useMemo 和 useCallback (opens new window)
  • 如何錯誤地使用 React hooks useCallback 來保存相同的 function instance (opens new window)

# 实战

  • 示例 - React (opens new window)
  • react-use (opens new window)

# 进阶

  • React Hooks in TypeScript (opens new window)
  • useEffect 完整指南 (opens new window)
  • useEffect vs. useLayoutEffect in plain, approachable language (opens new window)
  • 精读《React Hooks 最佳实践》 (opens new window)
  • 精读《怎么用 React Hooks 造轮子》 (opens new window)

# 扩展阅读

  • V8 将为 React hooks 改进数组解构的性能 (opens new window)

# Hooks 知识体系

# Hooks 主要概念

Hooks 主要是 useXXX 系列 API,
以及实现 Hooks 机制的 React 内部代码(resolveDispatcher 等)。

  • 常用 API
    • useContext
    • useState
    • useReducer
    • useRef
    • useEffect
    • useLayoutEffect
    • useCallback
    • useMemo

另外社区中还衍生出了更多 API:react-use (opens new window)

# Hooks 典型代码

# Hooks 大致用法

  • useState
    const Comp = () => {
      const [state, setState] = useState(initialState)
      const handler = (e) => setState(newState || prevState => nextState)
      return <div onClick={handler}>{state.myKey}</div>
    }
    
    1
    2
    3
    4
    5
  • useReducer
    const Comp = () => {
      const [state, dispatch] = useReducer(
        reducer,
        initialState,
      );
      return (
        <div onClick={() => dispatch(action)}>
          {state.myKey}
        </div>
      );
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • useCallback
    const Comp = () => {
      const sameFn = useCallback(fn, [byDeps]);
      return <div onClick={() => sameFn()}></div>;
    };
    
    1
    2
    3
    4
  • useMemo
    const Comp = () => {
      const sameResult = useMemo(fn, [byDeps]);
      return <div>{sameResult}</div>;
    };
    
    1
    2
    3
    4
  • useContext
    const MyContext = createContext(null);
    const Comp = () => {
      const color = useContext(MyContext);
      return <p style={{ color }}>Hello World</p>;
    };
    const App = () => (
      <MyContext.Provider value="green">
        <Comp />
      </MyContext.Provider>
    );
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • useRef
    const Comp = () => {
      const refer = useRef(null);
      return (
        <div
          ref={refer}
          onClick={() => refer.current.innerHTML}
        ></div>
      );
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • useEffect
    const Comp = () => {
      const sideEffectFn = () => {
        myLogic();
        return cancelSideEffectFn();
      };
      useEffect(sideEffectFn, [byDeps]);
      return <div></div>;
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
  • useLayoutEffect
    const Comp = () => {
      const sideEffectFn = () => {
        myDOMLogic();
        return cancelSideEffectFn();
      };
      useLayoutEffect(sideEffectFn, [byDeps]);
      return <div></div>;
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
  • useImperativeHandle
    const ChildInput = forwardRef((props, parentRef) => {
      const realRef = useRef(null);
      useImperativeHandle(parentRef, () => realRef.current);
      return (
        <input type="text" name="child input" ref={realRef} />
      );
    });
    
    1
    2
    3
    4
    5
    6
    7

# Hooks 相关

# 使用上的注意点

  • 函数组件,在函数体中使用 Hooks API。
  • 为了表现出清晰的逻辑,Hooks 最好在函数代码的顶层结构中使用。

# Hooks 大致原理

代码没有黑魔法

以 useState 为例,直接追踪代码关系(删减了部分细节代码)

// in 'react'
function useState(initialState) {
  var dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

function resolveDispatcher() {
  return ReactCurrentDispatcher.current;
}

// in 'react-dom'
HooksDispatcherOnMount = {
  useState: function (initialState) {
    return mountState(initialState);
  },
};
HooksDispatcherOnUpdate = {
  useState: function (initialState) {
    return updateState(initialState);
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

观察代码可以发现:
dispatcher 是 React 提供和维护的一个公共对象,
React 会在不同生命周期(mount、update)提供不同的 dispatcher,
而不同的 dispatcher 包含不同的(相应的)处理方法。

函数组件代码中看上去调用了同一个 Hooks API,
但实际上会在不同时期取到不同的方法,
也就实现了以前 Class 写法中的生命周期的效果。

# Hooks 和 Class 的差异

Hooks 写法和之前的 Class 写法算是截然不同的范式。

Runtime 层面的直观差异是:

  • Hooks 组件每次渲染时,函数内可能所有东西都是重新声明的。
    (比如依赖 setState 的简单的 onClickHandler)
  • Class 组件,render() 中的局部方法一般都指向原型上的同一个,不会重新声明。

这算是在内存和性能上的差异(注意不是好坏)。

根据这一状况,
Hooks API 自带了一些简单的优化措施(如 第二参数)

# 第二参数和优化

  • useEffect
  • useCallback
  • useMemo

以上方法都有第二个可选参数,用作依赖对比,目的是减少重复执行。
但是,依赖对比也需要一定的计算开销(在 React 内部,至少也有十来行)。

所以理解 JS 执行原理并加以权衡,
单行的简单 onClickSetStateHandler 可以不包裹到 useCallback 里。

可以只在以下情况使用 useCallback:

  • 避免重复执行
    • 需要同一个函数(如 timer)
    • 缓存大开销的函数(如 fetch)

也可以根据团队标准全部使用 useCallback,降低心智负担。

# Hooks 的运用

随着 Hooks 的普及和社区的发展,
函数组件能做的事情越来越多了,
甚至可以尝试只使用函数组件来写整个应用。

对于大型前端软件来说,也可以采取以下方案:

  • 直接由更专业的外部工具(如 redux、rxjs、ramda)
    来处理复杂逻辑业务(如 sideEffect 实体、fetch 竞态、memo 计算)
  • React 只负责渲染相关逻辑

这样也就不用太关心 Hooks 本身的优化技巧了。

需要根据实际情况灵活运用。

#react
上次更新: Jan 29, 2022 6:01 PM
最近更新
01
Linux Shell 快速入门笔记
11-18
02
我的 Web 前端开发知识体系 (2022)
01-29
03
游戏环境研究笔记(2022-01)
01-16
更多文章>
Theme by Vdoing | Copyright © 2019-2022 Seognil LC | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式