React Hooks 入门

目录

  • 什么是 React Hooks?
  • 为什么要创造 Hooks?
  • Hooks API 一览
  • Hooks 使用规则

什么是 React Hooks?

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.

  • Hooks 是在 React 16.8 版本的新特性,允许你以函数组件的形式去写每个组件
  • 每一个 Hook 是由 React 提供的函数,它可以让你在函数组件中“钩”连到一些 React 特性。

Class?生命周期函数?this?
—— 可以通通say goodbye


Hooks 会使 React 变得臃肿吗?

  • hook 不包含任何破坏性的更改,它提供了使用我们已知的 React 特性的能力,如 state 、context 和生命周期。
  • 减少编写 React 应用时需要考虑的概念数量。Hooks 可以使得你始终使用函数,而不必在函数、类、高阶组件和 reader 属性之间不断切换。
  • 随之而来,代码量也会减少。😊

为什么要创造 Hooks?

React 的使命:让开发者更容易地构建好的 UI。💪

为了这个目标,React 团队做了哪些努力呢?🌞

  1. 尝试简化复杂的东西(Suspense)
  2. 提升性能,尝试让 React 本身运行的更快(Time Slicing)
  3. 使用开发者工具帮助开发者 debug(Profile)

React 还存在什么糟糕的地方?

  • 📎 在组件间复用状态逻辑很难——嵌套地狱
    • mixin
    • props
    • 高阶组件
  • 📦 庞大的组件,逻辑分散杂乱无章,变得难以理解
  • 😓 令人困惑的 Classes

So,有没有什么方法来解决这三个问题呢?

Of course!React Hooks!🔥


内置的 Hook


Hook API 介绍

请参照 demo 示例 进行学习。

useState

传入值作为state的默认值,返回一个数组,数组的第一项是对应的状态(默认值会赋予状态),数组的第二项是更新状态的函数。

const [state, setState] = useState(initialState);
setState(newState);

不同于 setStateuseState 不会自动合并更新的对象,需要我们自己用对象扩展的形式合并,如果需要管理包含多个子值的状态对象,则用 useReducer 更为适合。

setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues};
});

Lazy initial state:如果初始状态是一个昂贵的计算的结果,你可以提供一个函数来进行计算,它只会在初始渲染时执行。

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

useEffect

可以在函数组件中执行副作用操作,并且是在函数渲染DOM完成后执行副作用操作,异步执行。

可以认为 useEffect 就是组合了 componentDidMountcomponentDidUpdate,以及 componentWillUnmount(在 useEffect 的回调中),但是又有区别,useEffect 不会阻止浏览器更新屏幕。

useEffect(() => {
  const subscription = props.source.subscribe();
  // 清理步骤,本质上就是消除副作用
  return () => {
    subscription.unsubscribe();
  };
}, [props.source]);

【注意】:永远要对 useEffect 的依赖诚实,被依赖的参数一定要填上去,否则会产生非常难以察觉与修复的 BUG。 (利用 eslint-plugin-react-hooks 自动订正你的代码中的依赖)

*useLayoutEffect

useEffect 相同,都是用来执行副作用,主要用来读取DOM布局并触发同步渲染,在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新,同步调用 effect。

官网建议还是尽可能的是使用标准的 useEffect 以避免阻塞视觉更新。


useCallback

可以认为是对依赖项的监听,接受一个回调函数和依赖项数组,返回一个该回调函数的 memoized(记忆)版本,该回调函数仅在某个依赖项改变时才会更新。

const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);

【用途】:解决将函数抽到 useEffect 外部的问题。

useMemo

主要用于渲染过程优化,两个参数依次是计算函数(通常是组件函数)和依赖状态列表,当依赖的状态发生改变时,才会触发计算函数的执行。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

【好处】:相比于 React.memo,可以做到更细粒度的优化渲染。如函数 Child 整体可能用到了 A、B 两个 props,而渲染仅用到了 B,使用 React.memo() 方案时,A 的变化会导致重渲染,而使用 useMemo 的方案则不会。

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)


useContext

useContext 接受一个 context 对象(由 createContext 创建)作为参数,并返回 Context.Consumer

  1. 创建一个 Context
const stateContext = createContext('default');
  1. 在根节点使用 Store.Provider 注入
function Parent() {
  const [count, setCount] = useState(0);
  return (
    <Store.Provider value={{ count }}>
      <Child />
    </Store.Provider>
  );
}
  1. 在子节点使用 useContext 拿到注入的数据
const Child = memo((props) => {
  const { count } = useContext(Store)
  // ...
});

【问题】:当函数多了,Provider 的 value 会变得很臃肿。
【解决】:使用 useReducer 解决这个问题。


useReducer

它和 Redux 的工作方式是一样的。useReducer 的出现是 useState 的替代方案,能够让我们更好的管理状态。

const [state, dispatch] = useReducer(reducer, initialArg, init);

【arg1】:reducer。
【arg2】:指定状态的默认值。
【arg3】:接受一个函数作为参数,并把第二个参数当作函数的参数执行。主要作用是初始值的惰性求值,把一些对状态的逻辑抽离出来,有利于重置state。

// 定义一个init函数
function init(initialCount) {
    return [
        ...initialCount,
    ];
}

// useReducer使用
useReducer(reducer,[{id: Date.now(), value: "Hello react"}], init);

useRef

返回一个可变的 ref 对象,其 .current 属性初始化为传递的参数(initialValue)。返回的对象将持续整个组件的生命周期。事实上 useRef 是一个非常有用的 API,许多情况下,我们需要保存一些改变的东西,它会派上大用场的。

const refContainer = useRef(initialValue);

*useImperativeHandle

可以让你在使用 ref 时自定义暴露给父组件的实例值。
当我们使用父组件把 ref 传递给子组件的时候,这个Hook 允许在子组件中把自定义实例附加到父组件传过来的 ref 上,有利于父组件控制子组件。

useImperativeHandle(ref, createHandle, [deps])

*useDebugValue

可用于在 React DevTools 中显示自定义钩子的标签。

useDebugValue(value)
function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  // Show a label in DevTools next to this Hook
  // e.g. "FriendStatus: Online"
  useDebugValue(isOnline ? 'Online' : 'Offline');

  return isOnline;
}

✌️ Hooks 的使用规则

  • 只能在函数最外层调用 Hook,不要在循环、条件判断或者子函数中调用。(🤔️ Why?
  • 只在React函数组件中调用 Hooks(除非是自定义Hooks)。

Hooks 内部是如何工作的:

// 伪代码
let hooks, i;
function useState() {
  i++;
  if (hooks[i]) {
    // 再次渲染时
    return hooks[i];
  }
  // 第一次渲染
  hooks.push(...);
}

// 准备渲染
i = -1;
hooks = fiber.hooks || [];
// 调用组件
YourComponent();
// 缓存 Hooks 的状态
fiber.hooks = hooks;

第一条规则可以确保每次组件呈现时调用钩子的顺序是相同的。
【注意】自定义 Hooks 从技术上讲并不是 React 的特性。编写自定义 Hooks 的可行性源自于 Hooks 的设计方式。


封装 http hook

import { useState, useEffect } from 'react';

export const useHttp = (url, dependencies) => {
  const [isLoading, setIsLoading] = useState(false);
  const [fetchedData, setFetchedData] = useState(null);

  useEffect(() => {
    setIsLoading(true);
    console.log('Sending Http request to URL: ' + url);
    fetch(url)
      .then(response => {
        if (!response.ok) {
          throw new Error('Failed to fetch.');
        }
        return response.json();
      })
      .then(data => {
        setIsLoading(false);
        setFetchedData(data);
      })
      .catch(err => {
        console.log(err);
        setIsLoading(false);
      });
  }, dependencies);

  return [isLoading, fetchedData];
};

能在 useEffect 中使用 async/await 吗?

【结论】:不能在 useEffect 中返回一个 promise。JavaScript 异步函数总是返回一个promise,而 useEffect 应该只返回另一个函数,该函数用于清除效果。也就是说,如果你在useEffect 中启动 setInterval,你将返回一个函数(这里有一个闭包)来清除 interval。

➹ 参考



最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 228,363评论 6 532
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,497评论 3 416
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 176,305评论 0 374
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,962评论 1 311
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,727评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,193评论 1 324
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,257评论 3 441
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,411评论 0 288
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,945评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,777评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,978评论 1 369
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,519评论 5 359
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,216评论 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,642评论 0 26
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,878评论 1 286
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,657评论 3 391
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,960评论 2 373