React Hooks 从相识到相爱

前言

自从 2019.2.6 React 16.8 发布以来,新特性 Hooks 彻底颠覆了以往的开发模式,我最早使用 Hooks 是在两年前,现在我们团队也在项目中大量使用该特性,并沉淀了一套 Hooks 底盘。

今天的分享主题叫《React Hooks 从相识到相爱》,整个分享我会以一条故事线的形式展开,初次相识 -> 萌生心动 -> 主动约会 -> 大胆表白。我会带大家从完全不知道什么是 Hooks,到喜欢上她,并能够在实际工作中用起来。

大家应该都写过函数组件,函数组件是没有状态的,如下是一个计数组件的示例,我想这个需求大家应该10s钟就能够写出来~

计数器示例.gif
import React from "react";
import { View, Text, Button } from "react-native";

function CountComponent(props) {
  return (
    <View>
      <Text>You clicked {props.count} times</Text>
      <Button onClick={() => props.addCount()}>Click me</Button>
    </View>
  );
}

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <CountComponent
        count={this.state.count}
        addCount={() => {
          this.setState(this.state.count + 1);
        }}
      />
    );
  }
}

我们封装了一个 CountComponent 组件,状态由 App class 组件控制,是一个完全受控组件。

细心的同事应该发现了,上面的代码有点冗余是吧,本质上 count 状态是可以直接封装在 CountComponent 里面的,不用放在父组件里面传进去。

但是在 React 16.8 之前,React 只给类组件提供了管理状态的功能,函数组件无法独立完成状态管理的工作。

而这个现状随着 React 16.8 的 Hooks 的到来也被打破了。

React Hooks 可以让你在不编写 Class 的情况下使用 state 以及生命周期等 React 特性。

Hook 这个单词的意思是"钩子"。它的作用是可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。 React Hooks 其实就是一系列钩子函数。

类组件通过继承 React.Component 获得 state 以及生命周期等功能来连接到 React 状态机里面;而 React Hooks 则是通过一系列钩子达到同样的目的。

用一张图让大家对 Hooks 有更清晰的认识:

React.Component 和 React Hooks 对比.png

通过 Hook 把上面的计数组件状态迁移到 CountComponent 组件中:

import React, { useState } from "react";
import { View, Text, Button } from "react-native";

function CountComponent() {
  const [count, setCount] = useState(0); // 非常陌生的代码
  
  return (
    <View>
      <Text>You clicked {count} times</Text>
      <Button onPress={() => setCount(count + 1)}>Click me</Button>
    </View>
  );
}

export default class App extends React.Component {
  render() {
    return <CountComponent />;
  }
}

useState 就是我们即将要学习的一个 Hook,我们先不管这个玩意是干什么用的,从表面来看我们可以简单的对这个 Hook 得出几个结论:

  • useState 返回一个数组,这个数组里面有两个元素,通过 ES6 的数组解构赋值语法分别赋给了 countsetCount
  • count 会在 CountComponent 重新渲染的时候获取最新的状态,类似于 Class 中的 this.statesetCount 用于更新 count 触发重新渲染,类似于 Class 中的的 this.setState

关于 ES6 数组解构语法,目的是为了让写法更简洁。除了数组可以解构,对象也是可以解构的,这里就不展开了,有兴趣可以去查看下 ECMAScript 6 的文档。

让我们对 hooks 的第一印象做个总结

  • Hooks 是 React 16.8 的新增特性,它可以让函数组件拥有 state 以及生命周期等 React 特性。
  • Hooks 其实是一系列”钩子“函数
  • 代码更少、写法更简单(一个简单的函数、不用继承、写构造函数等)
1、更符合”函数式“思想

React 组件一直更像是函数,而 Hook 则拥抱了函数

如果大家看过 React 的设计思想,就会发现 React 强调最多的就是函数,传入一个状态,得到一个UI。
而 React Hooks 就是一系列函数,所以更符合 React 的设计思想,也是 React 大力推行 Hooks 的原因之一。

2、类组件冗余的继承和构造函数

每一个 Class 组件都要继承 React.Component,以此获得 state、生命周期等 React 能力:

class CustomComponent extends React.Component {
    constructor(props) {
      super(props)
        this.state = {
          a: 0
        }
    }

    render() {
        return (
            <View>
                <Text>{this.state.a}</Text>
            </View>
        )
    }
}

大家写习惯了可能觉得没啥了,那我们来对比下 hooks 的代码:

const CustomComponent = function() {
    const [a, setA] = useState(0)

    return (
        <View>
        <Text>{a}</Text>
      </View>
    )
}

这里用 hooks 实现了功能一样的组件,大家可以先不用关注实现,可以看到 hooks 的写法明显简洁很多,没有继承、没有构造函数、没有 render 重写等等。其实函数组件整体的代码量都比类组件要少。

我这里把 类组件 和 函数组件 压缩后的代码进行了对比:

类组件做了语法糖的包装,原型扩展、super、this 的处理等等,就拿刚才那个组件来说,函数组件的整体代码量大概只有类组件的一半,减少了50%。

当然复杂组件达不到这个降幅,但是不可否认是,函数组件对包大小的优化是有明显收益的,所以如果你想要减少包大小,那么现在就开始使用 Hooks 吧。

3、类组件混乱的生命周期函数

我们知道类组件有一大堆生命周期方法,这幅图展示是 React 16 之前的生命周期图,大家应该都比较熟悉:

有人可能会问,这里面不是有些生命周期函数被废弃了嘛?是的,在 React 16 的时候 ,为了适配 Fiber 架构,废弃了几个render期的生命周期函数。

所以 15 和 16 的生命周期流程是不一样的!

上面用黄色标记的就是 React 16 废弃的生命周期函数,为了避免开发者再次使用,React 直接在这些生命周期函数前加上了 UNSAFE_ 前缀,意指不安全的。

什么是不安全的?为什么要废弃?

具体为什么是不安全的,大家可以去看 Fiber 架构说明,这里就不展开。
然后你以为这就结束了?React 在废弃这三个生命周期函数的同时,又新增了两个生命周期函数.... 如下:

新增的两个函数,一个是静态函数 getDerivedStateFormProps,一个 getSnapshotBeforeUpdate。这两又是干啥的?

看到这里是不是已经懵了,这么多生命周期函数该怎么用? 感觉脑壳都疼了。

反观 Hooks,React Hooks 弱化了生命周期函数的复杂概念,将 React 各个渲染节点注入到钩子里面去了,大大简化了上手难度。

我们只要掌握了 useStateuseEffect 基本就可以打天下了。

4、this 指向问题

this 指向问题是 JS 历来的坑点,ES5 判定 this 指向可以通过查看 new调用、显示绑定(callbindapply)、默认绑定 等等因素决策,但 ES6 之后有了箭头函数和类,this 绑定的规则也有所改变。

当然 this 问题并不是只在 类组件中存在,但是 类组件会蒙蔽开发者,让他们以为自己拿到的 this 是你认为对的 this,导致出现一些奇怪的问题而不知道原因。

5、业务逻辑分散

类组件的第五个问题是业务逻辑过于分散。

下面这个例子中,我要给组件添加一个定时器和一个事件监听,我们需要把监听事件卸载 componentDidMount 里面,同时将返回的实例赋值给 class 的实例变量。然后我们还需要在 componentWillUnMount 中拿到实例变量去取消定时器和事件监听。

componentDidMount() {
    // 定时器
    this.timer = setTimeout(() => {
     // do something
    }, 1000)
   // 事件监听
    this.listener = DeviceEventEmitter.addListener("key", () => {
     // do something
  })
}

componentWillUnmount() {
    // 取消定时器 
  this.timer && clearTimeout(this.timer)
    // 取消事件监听
  this.listener && this.listener.remove()
}

可以发现,实现一个功能,代码要写在不同生命周期函数里面,一点都不聚合,特别的分散,当代码量上去了之后就会增加维护难度。很可能监听了一个事件,然后忘记写移除代码。

再来看看 React Hooks 的逻辑:

useEffect(() => {
  // 定时器
  const timer = setTimeout(() => {
    // do something
  }, 1000)
  // 事件监听
  const listener = DeviceEventEmitter.addListener("key", () => {
    // do something
  })
  return () => {
    // 取消定时器 
    timer && clearTimeout(timer)
    // 取消事件监听
    listener && listener.remove()
  }
}, [])

这里的事件监听和移除监听是写在同一个闭包里面的,作为一个整体进行展示,是不是聚合很多~

6、逻辑复用困难

前面的几点都是前菜,React Hooks 相对于传统组件的最大优势其实是逻辑复用。说到逻辑复用,很多同学会说 Render PropsHOC(高阶组件)可以做逻辑复用!那我们看看 类组件 的逻辑复用有多么的惨不忍睹。

复用技术一:Render Props

首先是 Render Props,有些同学可能不太清楚什么是 render props。那我这里简单介绍一下,react 官方文档是这样描述的:

什么是 Render Props?这是一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术。

光看这个介绍有点蒙,相信使用过 RN 的应该都知道 FlatList 吧,其中的 renderItem 就是利用了 render props 技术,将设置给 FlatList 组件的数据源共享给列表中的每个 Item 组件。

<FlatList renderItem={(item) => {
    return <Item data={item.data}>
}}></FlatList>

renderItem 是一个函数,item 会被传递到相应的 Item 组件中,这就实现了实现了两个组件之间的数据共享。

更多关于 render props 的说明可以查看官方 Render Props 文档。

复用技术二:HOC(高阶组件)

除了 render props,第二个大家最常用组件复用技巧就是 HOC,HOC 是 Higher Order Component 的缩写,意为高阶组件。

官方的解释是:

HOC 是 React 中用于复用组件逻辑的一种高级技巧,具体而言,高阶组件是参数为组件,返回值为新组件的函数

表现形式为:

const EnhancedComponent = higherOrderComponent(WrappedComponent);

higherOrderComponent 这个新函数会对 WrappedComponent 组件进行包装,注入 props。

react-redux 中的 connect 就是 HOC 的典型应用,用于连接 React 组件与 Redux store,接受数据流、dispatch 更新数据等操作

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

更多关于 HOC 的说明可以查看官方 高阶组件(HOC) 文档。

那简单介绍了一下 Render Props 和 HOC,接下来用一个实际例子来对比 Render Props、HOC、Hooks 的逻辑复用代码的优劣势。

Render Props 的缺陷:嵌套地狱

首先我们这里想复用监听 PC浏览器窗口大小 的变化逻辑,用 render props 可以这样写:

<WindowSize>
  {(size) => (<MyComponent windowSize={size}>)}
</WindowSize>

然后,我又想复用监听鼠标位置的逻辑,我只能这么写了:

<WindowSize>
  {(size) => (
    <MousePosition>
      {(position) => <MyComponent windowSize={size} mousePosition={position}>}
    </Mouse>
  )}
</WindowSize>

到这里大家应该看到了问题所在。我再复用一个逻辑,那就的再嵌套一层,一层层嵌套下去,这就是所谓的嵌套地狱

HOC 的缺陷:可读性差,容易属性冲突

同样一个逻辑复用,用 HOC 可以这样写:

export default windowSize(mousePosition(MyComponent))

一行代码搞定~,感觉很轻松的样子,但是仔细一看,代码可读性并不高,函数嵌套调用,如果再来一个逻辑复用,那么就是 嵌套-嵌套-嵌套….

问题的重点还不在这里,HOC 最被诟病的是属性冲突,想象一下,大家都在往组件的 props 上面挂属性,万一有个重名的,那就 GG 了!

Hooks 惊艳亮相:秒杀 Render Props 和 HOC
const size = useWindowSize()
const position = useMousePosition()

return <MyComponent windowSize={size} mousePostion={postion}>

React Hooks 直接把逻辑复用代码平铺了,避免了嵌套问题,同时代码可读性非常高,添加逻辑基本对原有逻辑没有太大改动。直接将 Render Props 和 HOC 都秒杀了!

到这里想必大家已经对 React Hooks 心动了吧,这就对了,接下来就带大家一起来看看 Hooks 的常用 API,揭开 React Hooks 的面纱~

1、useState

useState 类似 class 的 this.statethis.setState 的功能,用于数据存储,派发更新。

useState 的函数声明如下,接受一个可选的参数,返回一个数组:

function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];

还是以上面的计数组件为例来看看 类组件 和 函数组件 的写法:

类组件
class CountComponent extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        count: 0
      };
    }
  
    render() {
      return (
        <View>
            <Text>You clicked {count} times</Text>
            <Button onPress={() => this.setState(this.state.count + 1)}>Click me</Button>
        </View>
      );
    }
  }
函数组件
function CountComponent() {
    const [count, setCount] = useState(0);
    
    return (
      <View>
        <Text>You clicked {count} times</Text>
        <Button onPress={() => setCount(count + 1)}>Click me</Button>
      </View>
    );
  }

上面是传统的类组件、下面这个是借助了 hooks 的函数组件。

正常情况下,每当 CountCompoent 重新渲染时(比如父组件重新渲染了,就会触发这个组件重新执行), useState 都会重新执行,那么它返回的两个元素应该都会被重置掉才对,所以 count 的值应该永远都是 0 !

你们分析的没错,正常情况下是这样的,但是 useState 不是普通函数,他是能够跟 React 组件生命周期关联的,只要组件挂载后没有被销毁,那么它返回的值一直会被缓存起来。

有人可能会好奇这个值究竟是怎么被缓存起来的,这个问题涉及到 React Hooks 的底层实现,是另一个话题了,我们在这里暂时不讨论。大家只需要知道 useState 不是普通函数,而是能够跟 React 生命周期绑定的钩子函数就行。

所以我们用 useState 就能够实现上面 类组件 中 this.statethis.setState 同样的功能,是不是简洁很多。

2、useRef

useRef 类似类组件中实例属性的功能,用来在组件生命周期内存储值。

useRef 的函数声明,它接收一个初始值 initialValue,返回一个 MutableRefObject,这个东西其实是一个包装了 current 属性的 JS对象,initialValue 初始值会直接赋值给 current。:

interface MutableRefObject<T> {
    current: T;
}

function useRef<T>(initialValue: T): MutableRefObject<T>;

useRef 返回的 JS对象 在组件的整个生命周期内持续存在,当组件重新渲染时对象不会被垃圾回收掉(普通变量就会被垃圾回收,因为函数作用域已经出栈了),我们可以通过 current 属性存储、读取缓存值。

那什么值需要在组件整个生命周期内存储呢?典型的就是组件 ref 实例,useRef 也正是因此而得名。下面用 useRef 存储了 TextInput ref 实例,当我们点击按钮的时候,就可以拿到这个对象,通过 current 访问实例,调用 onFocus 方法让输入框获得焦点:

function MyComponent() {
    const inputRef = useRef(null)
    
    const onButtonClick = () => {
      inputRef.current.onFocus()
    };
  
    return (
        <View>
        <TextInput ref={inputRef}/>
        <Button onPress={onButtonClick}>focus</Button>
      </View>
    );
}
3、useEffect

useEffect 用于执行副作用,可以把 useEffect 看做类组件中 componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个生命周期函数的组合。

那什么是副作用?

副作用是函数式编程中的概念,与之对立的概念是”纯函数“,纯函数的特点就是相同的输入值时,需产生相同的输出,这个输出不光是返回值,还包括是否影响外部的状态,比如全局变量、DOM结构、日志输出、网络请求等等。如果一个函数不满足这个特质,那么我们就说它含有副作用。

因此,React 中的 useEffect,就是用于执行副作用的函数,诸如网络请求、事件监听、DOM修改等操作。如果你的动作不具备副作用,可能就没必要放到 useEffect 中了。(比较抽象,大家可以后面再实践中慢慢体会)

useEffect 的函数声明如下:接受两个参数,第一个参数是一个函数,我们称之为副作用回调函数,会在副作用触发时自动执行,该函数可以再返回一个函数做副作用的清除工作;第二个参数是一个数组,用于给出 Effect 的依赖项,只要这个数组中的变量任意一个发生变化,就会触发副作用,执行第一个函数参数:

function useEffect(effect: EffectCallback, deps?: DependencyList): void;

下面我们用 useEffect 模拟一下 React 组件的声明周期函数:

模拟 componentDidMount

当依赖数组传一个空数组,那么 useEffect 副作用回调只会在第一次挂载时执行。

下面的例子是在组件挂载时添加一个定时器:

useEffect(() => {
  // 只会在组件第一次挂载时执行
  this.timer = setTimeout(Function, 1000)
}, [])

这个时候副作用回调函数就相当于 componentDidMount 的生命周期函数了。

模拟 componentWillUnmount

useEffect 副作用回调函数可以有一个返回函数,这个函数会在组件销毁的时候执行。

上面我们添加了定时器,那么在组件卸载时就需要进行清除工作:

useEffect(() => {
  // 添加定时器
  this.timer = setTimeout(Function, 1000)
  return () => {
   // 清除定时器
   this.timer && clearTimeout(this.timer)
  }
}, [])

副作用回调函数返回的函数,就相当于 componentWillUnmount 生命周期函数了。

模拟 componentDidUpdate

当依赖数组如果不传,那么组件挂载、更新都会执行副作用回调函数。

但是 componentDidUpdate 只有在组件更新时才会调用,挂载时是不会执行的,所以要模拟 componentDidUpdate 还需要借助 useRef Hook。

考虑定时器需要从父组件接受等待时间参数的场景:

// 初始值为 true
const isFirst = useRef(true)

useEffect(() => {
  if (!isFirst.current) {
    // 会在组件更新时执行
    this.timer = setTimeout(Function, props.delay)
    return () => {
      this.timer && clearTimeout(this.timer)
    }
  }
  // 执行一次之后设置为 false
  isFirst.current = false
})
useEffect 依赖数组的应用

上面的代码逻辑其实是有问题的,what?细心的同事应该已经发现了~,主要有以下两个问题:

  • 这个定时器在组件挂载时是不会开启的,只有在更新时才会触发;
  • 每次组件更新,定时器都会无条件的销毁重设,没有条件约束,影响性能。

componentDidUpdate 通常像下面这样处理:

componentDidUpdate(prevProps) { 
  if (this.props.delay !== prevProps.delay) {    
    this.timer = setTimeout(Function, props.delay);  
  }
}

但是 hook 中并不能像 类组件 一样通过 prevProps 访问上一次的 props 值,如何做 props.delay 的对比?

Hooks 考虑到这种场景,已经将这个功能内置到其 API 中去了,通过第二个数组参数进行控制,上面的代码可以这样改造:

useEffect(() => {
  // 挂载时、props.delay 更新时执行
  const timer = setTimeout(Function, props.delay)
  return () => {
    timer && clearTimeout(timer)
  }
}, [props.delay])

useEffect 内部缓存了上一次的状态,在组件重新渲染的时候,会自动对数组中的元素进行一次浅比较,任意一个元素变更都会执行副作用回调函数。

让我们来做一个小结:
  • Hook 的最大特点就是跟 React 组件的生命周期关联,这也是 Hooks 能够让函数组件能够管理状态的根本;
  • useStateuseRefuseEffect 是 React 最基本的三个 Hook,掌握了这三个 Hook 的应用基本就可以开始撸代码了;
  • React Hooks API 中目前总共有 13 个 Hooks,用法同上面讲到的 Hook 用法都差不多,大家可以后期慢慢深入了解。

你以为这就结束了吗,是不是已经被 React Hooks 打动了呢?接下来才是真正让你倾心的时刻,接着往下看~

React Hooks 最强大功能当属自定义 Hook,它是 React Hooks 风靡的关键。

什么是自定义 Hook?自定义 Hook 是一个函数,其名称约定以 “use” 开头,函数内部可以调用其他的 Hook

当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。而组件和 Hook 都是函数,所以也同样适用这种方式。我们可以将共享逻辑提取到自定义 Hook 中去,前面提到的 useWindowSizeuseMousePosition 都属于自定义 Hook,他们分别封装了窗口大小变化、鼠标位置变化的逻辑。

为了了解自定义 hook,简单看看我们前面提到的 useWindowSize 自定义 Hook 的代码实现:

const useWindowSize = () => {
  const [windowSize, setWindowSize] = useState({ width: 0, height: 0 })
  
  useEffect(() => {
    const onWindowResize = () => {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight
      })
    }
    window.addEventListener('resize', onWindowResize)
    return () => {
        window.removeEventListener('resize', onWindowResize)
    }
  }, [])
  
  return windowSize
}

到这里有的人可能并没有看出 Hooks 的优势,因为没有对比。我们以日常最常见的网络请求为例,看看 Hooks 是怎么让大家能够早点下班的~

class 版本的网络请求:
export default class MyPage extends React.Component {
  request = null // fetch 实例

  constructor(props) {
    super(props);
    this.state = {
      data: null,
      errorMsg: null,
      errorCode: null,
      loading: false
    }
  }
  
  componentDidMount() {
    // 组件挂载,发起网络请求
    this.request = this.requestData()
  }

  componentWillUnmount() {
    // 组件销毁,取消网络请求
    this.request.cancel()
  }
  
  requestData = () => {
    this.setState({
      loading: true
    })
    return fetch("https://xxx").then((result) => {
      this.setState({
        data: result.data,
        errorMsg: null,
        errorCode: null
      })
    }).catch((error) => {
      this.setState({
        errorMsg: error.message,
        errorCode: error.code
      })
    }).finally(() => {
      this.setState({
        loading: false
      })
    })
  }
}

render() {
  return <View>
    {this.state.loading && <Loading/>}
    {(!this.state.loading && this.state.errorMsg) && <Error msg={this.state.errorMsg} code={this.state.code}></Error>}
    {(!this.state.loading && !this.state.errorMsg) && <Content data={this.state.data}></Content>}
  </View>
}

是不是感觉这种模板代码每个页面都有,简直是烦死了…,那为什么不封装一下呢?因为不好封装!

这段逻辑其实是把 状态逻辑UI显示 耦合在一起了,要分开其实挺难的,不管是 render props 还是 HOC,封装出来的效果都差强人意。而拆解状态和UI正是 Hooks 的强项。

Hooks 版本的网络请求:
export default function MyPage() {
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(false)
  const [errorMsg, setErrorMsg] = useState(null)
  const [errorCode, setErrorCode] = useState(null)
  const request = useRef(null)

  const requestData = useCallback(() => {
    setLoading(true);
    return fetch("https://xxx")
      .then((result) => {
        setData(result.data);
        setErrorMsg(null);
        setErrorCode(null);
      })
      .catch((error) => {
        setErrorMsg(error.message);
        setErrorCode(error.code);
      })
      .finally(() => {
        setLoading(false);
      });
  }, []);

  useEffect(() => {
    request.current = requestData()
    return () => {
      request.current.cancel()
    }
  }, [])

  return <View>
    {loading && <Loading/>}
    {(!loading && errorMsg) && <Error msg={errorMsg} code={code}></Error>}
    {(!loading && !errorMsg) && <Content data={data}></Content>}
  </View>
}

单从代码数量来看, hooks 只是比 类组件 中的网络请求少一些代码而已,并没有太大优势对吧,接下来就是见证奇迹的时刻~

咱们自定义一个 Hook,取名为 useRequest,这个自定义 Hook 接收一个 url 参数,返回网络请求的最终结果,包括数据、加载状态、错误信息等等。代码如下:

const useRequest = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [errorMsg, setErrorMsg] = useState(null);
  const [errorCode, setErrorCode] = useState(null);
  const request = useRef(null);

  const requestData = useCallback(() => {
    setLoading(true);
    return fetch(url)
      .then((result) => {
        setData(result.data);
        setErrorMsg(null);
        setErrorCode(null);
      })
      .catch((error) => {
        setErrorMsg(error.message);
        setErrorCode(error.code);
      })
      .finally(() => {
        setLoading(false);
      });
  }, [url]);

  useEffect(() => {
    request.current = requestData();
    return () => {
      request.current.cancel();
    };
  }, []);

  return { data, loading, errorMsg, errorCode }
};

可以看到这里面的代码都是从上面挪过来的,只有 url 是从外面传进来的,这也是 Hooks 的强大之处,就跟抽取复用函数一样简单。

那我们怎么使用呢?在我们的 MyPage 中这样使用:

export default function MyPage() {
 const { data, loading, errorMsg, errorCode } = useRequest("https://xxx")

 return <View>
    {loading && <Loading/>}
    {(!loading && errorMsg) && <Error msg={errorMsg} code={code}></Error>}
    {(!loading && !errorMsg) && <Content data={data}></Content>}
  </View>
}

简直不要太简单,可以早点下班了~,到此,相信你已经彻底爱上了 React Hooks,那就不要再腼腆了,直接表白吧~~~

基于 Hooks 这种强大的能力,我们可以抽象大量自定义 Hooks,让代码更加简单,同时也不会增加嵌套层级。

到这里分享就结束了,这篇文章主要跟大家分享 React Hooks 的入门篇,后续还会有实战篇,下次再会~

本文原创,转载注明出处

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

推荐阅读更多精彩内容