专栏名称: 前端外刊评论
最新、最前沿的前端资讯,最有深入、最干前端相关的技术译文。
目录
相关文章推荐
商务河北  ·  经开区“美•强•优”三重奏 ·  12 小时前  
前端早读课  ·  【第3453期】圈复杂度在转转前端质量体系中的应用 ·  22 小时前  
奇舞精选  ·  从 DeepSeek 看25年前端的一个小趋势 ·  昨天  
奇舞精选  ·  从 DeepSeek 看25年前端的一个小趋势 ·  昨天  
前端早读课  ·  【第3452期】React 开发中使用开闭原则 ·  昨天  
51好读  ›  专栏  ›  前端外刊评论

一个很有意思的 hook 库:react-hanger

前端外刊评论  · 公众号  · 前端  · 2019-03-15 07:00

正文

前言

千呼万唤始出来,React Hooks终于在React 16.8版本中发布稳定版了。最近逛github发现了一个很有意思的库:react-hanger。

复习React Hooks

如果对Hooks还不怎么了解的同学,建议去看一下官方文档:Introducing Hooks.

什么是 Hooks?

我们都知道,在Hooks之前,开发react组件主要是class组件和function组件。function组件没有state,所以也叫SFC(stateless functional component),简单的将props映射成view;class组件有state,能够处理更加复杂的逻辑。但是基于class的组建并不是完美的,总结起来就像Dan说的那样,有三个主要的问题:

  1. 代码重用:在hooks出来之前,常见的代码重用方式是HOCs和render props,这两种方式带来的问题是:你需要解构自己的组件,非常的笨重,同时会带来很深的组件嵌套

  2. 复杂的组件逻辑:在class组件中,有许多的lifecycle 函数,你需要在各个函数的里面去做对应的事情。这种方式带来的痛点是:逻辑分散在各处,开发者去维护这些代码会分散自己的精力,理解代码逻辑也很吃力

  3. class组件的困惑:对于初学者来说,需要理解class组件里面的this是比较吃力的(这个理由有点勉强~),同时,基于class的组件难以优化(举个不恰当的例子,看一下babel转移出来的class代码量增长了多少)

为了解决上面的这三个问题,react hooks提案登场了,它有以下几个特点:

  1. 无痛接入,不破坏现有的项目结构

  2. 完全向后兼容,不包含任何不兼容breaking changes

  3. 现在就能使用

Hooks 允许你在不编写 class 的情况下使用状态(state)和其他 React 特性。 你还可以构建自己的 Hooks, 跨组件共享可重用的有状态逻辑。

现在React中内置的Hooks有:

  • Basic Hooks

    • useState

    • useEffect

    • useContext

  • Additional Hooks

    • useReducer

    • useCallback

    • useMemo

    • useRef

    • useImperativeHandle

    • useLayoutEffect

    • useDebugValue

当然了,授之以鱼不如授之以渔,React官方也提供了教你如何封装自己Hook的文档Building Your Own Hooks,有兴趣的小伙伴可以去阅读一下。

react-hanger初窥

大致的看了下react-hanger的源码之后发现,这个库其实是对React Hooks API的适用性封装。暴露一些更常用的Hooks节省大家造轮子的工作量。

React的核心开发者Dan看到这个库也做了评价:

一个对Hooks的隐喻。你可以将你的state“挂起”在你的function component上,等你回来的时候,它就挂在那。

本文写作时,react-hanger的Usage里提供了6个API,从名字里就可以看出这些Hook都是做什么的(Hooks都以"use"开头,这是一种约定),

  1. import {

  2. useInput,

  3. useBoolean,

  4. useNumber,

  5. useArray,

  6. useOnMount,

  7. useOnUnmount

  8. } from "react-hanger";

使用起来也很简单,比如 useNumber

  1. const App = () => {

  2. const showCounter = useBoolean(true);

  3. const counter = useNumber(0);


  4. return (

  5. <button onClick={counter.increase}> increase button>

  6. {showCounter.value && {counter.value} span>}

  7. <button onClick={counter.decrease}> decrease button>

  8. div>

  9. );

  10. };

  11. 初步印象:大致与原始的basic hooks有点不同的是,useState返回一个数组,分别是 操作,而react-hanger提供的API貌似是将 一些操作封装到一个对象中,比如 counter就是一个 {value:count,increase:setCount(count+1),decrease:setCount(count-1)}的对象。

    还有更多的操作方法可以看react-hanger的sandbox:https://codesandbox.io/s/44m70xm70

    react-hanger源码浅析

    其实翻看了react-hanger的源码之后会发现,react-hanger一共引用了四个React内置的Hook,

    1. import { useCallback, useEffect, useRef, useState } from "react";

    然后返回一些“轮子”hooks,包括 useNumber useArrayuseBoolean等等。

    这些轮子可以大致分为两类:封装Hook和拆分Hook。

    封装Hook

    比如 useStatefuluseNumberuseArrayuseBoolean都是对内置Hook useState的封装。

    useStateful

    1. export const useStateful = initial => {

    2. const [value, setValue] = useState(initial);

    3. return {

    4. value,

    5. setValue

    6. };

    7. };

    利用ES6的解构赋值,将 useState 返回的数组封装成一个对象重新返回,方便调用。

    useNumber

    1. export const useNumber = (

    2. initial,

    3. { upperLimit, lowerLimit, loop, step = 1 } = {}

    4. ) => {

    5. const [value, setValue] = useState(initial);

    6. return {

    7. value,

    8. setValue,

    9. increase: useCallback(i => {

    10. setValue (...);

    11. }, []),

    12. decrease: useCallback(d => {

    13. setValue(...);

    14. }, [])

    15. };

    16. };

    useNumber接收一个initial number和一个配置项对象,在内部是通过对initial number进行useState Hook,返回一个对象,除了基本的 valuesetValue,还有两个方法 increasedecrease。这两个方法都是用 useCallbacksetValue进行的进一步封装。

    useCallback是一个比较重要的内置Hook, useCallback 的可以于缓存了每次渲染时 inline callback 的实例,在第二个参数数组内的值发生更改时才会更改。 这样可以配合上子组件的 shouldComponentUpdate 或者 useMemo 起到减少不必要的渲染的作用。

    而第二个参数为空数组的意思就是告诉React不管参数如何都要记忆。

    useArray & useBoolean & useInput

    至于 useArray useBooleanuseInput这三个hook可以说和 useNumber大同小异,都是需要一个传入的initial值,在hook内部通过 useState初始化,再返回一些常用的操作方法。

    这里的 useInput是针对于受控组件,所以不需要 useRef

    useSetState

    1. export const useSetState = initialValue => {

    2. const { value, setValue } = useStateful(initialValue);

    3. return {

    4. setState: useCallback(v => {

    5. return setValue(oldValue => ({

    6. ...oldValue,

    7. ...(typeof v === "function" ? v(oldValue) : v)

    8. }));

    9. }, []),

    10. state: value

    11. };

    12. };

    Unlike the setState method found in class components, useState does not automatically merge update objects. 与类组件中的setState方法不同,useState不会自动合并更新对象。

    熟悉React Hook的同学看了代码就知道这个hook是封装了什么了,因为useState返回的类似于 setCount的方法不会自动合并更新对象。这个hook帮助大家可以获得一个可以merge之前value的Hook型 setState

    拆分Hook

    上述几个算是封装hook,那么下面的几个就可以算是拆分hook,对 useEffect更精细化的处理。

    useOnMount & useOnUnmount

    众所周知, useEffect是被用来处理一些原先放在class组件中生命周期函数的副作用,比如 componentDidMountcomponentDidUpdatecomponentWillUnmount,集合而成的一个Hook。

    理论上,在每次渲染后都会触发 useEffect 的效果,但是如果我只想在didmount里或者只想在willunmount里做一下事情,该怎么办?

    这时就用到了 useEffect的一个特点:第二个参数为效果依赖的值数组,也就是说只有当数组内的值变化才会触发 useEffect

    1. useEffect(

    2. () => {

    3. const subscription = props.source.subscribe();

    4. return () => {

    5. subscription.unsubscribe();

    6. };

    7. },

    8. [props.source],

    9. );

    而如果第二个参数为一个空数组的时候,则相当于告诉React你的效果不依赖于组件中的任何值,因此该效果只能在mount上运行并在unmount上清理,它不会在更新时运行。

    1. export const useOnUnmount = onUnmount =>

    2. useEffect(() => {

    3. return () => onUnmount && onUnmount();

    4. }, []);


    5. export const useOnMount = onMount =>

    6. useEffect(() => {

    7. onMount







请到「今天看啥」查看全文