React官方在 2018 ReactConf 大会上宣布 React v16.7.0-alpha(内测) 将引入 Hooks。什么是Hooks,我们来了解一下。

什么是Hooks?

在平时开发过程中,我们一般都会遇到如下问题:

1. 难以重用和共享组件中的与状态相关的逻辑2. 逻辑复杂的组件难以开发与维护,当我们的组件需要处理多个互不相关的 state 时,每个生命周期函数中可能会包含着各种互不相关的逻辑在里面。3. 由于业务变动,函数组件不得不改为类组件等等。复制代码

上面说起来比较抽象,接下来我们以 键盘Api Keyboard 为例说明问题。

export default class App extends Component {constructor(props) {super(props);this.state = {isShowKeyboard: false}}static getDerivedStateFromProps() {this.keyboardDidShowListener = Keyboard.addListener("keyboardDidShow",this.keyboardDidShowHandler);}keyboardDidShowHandler () {this.setState({isShowKeyboard: true});}componentWillUnmount() {this.keyboardDidShowListener.remove();}render() {return (<View style={styles.container}><Text>当前键盘状态: {this.state.isShowKeyboard}</Text></View>);}
}复制代码

上面的代码用例比较简单,使用 Keyboard 注册监听键盘的显示、隐藏状态。可以看到键盘事件的注册,注销,状态的render都放在了Component中,如果当前Component中涉及很多这样的逻辑,会造成当前Component职责非常重,并且状态数据不能共享,当在另一个Component中需要监听键盘事件时,需要重新编写或Copy重复代码,冗余非常严重,对于功能维护和扩展都不是一件好事。 Hooks的出现解决了上面的问题,它允许开发者定义函数组件,也可以使用类组件(class components)的 state 和 组件生命周期,而不需要在 mixin、 函数组件、HOC组件和 render props 之间来回切换。方便我们在业务中实现业务逻辑代码的分离和组件的复用。与使用 setState 相比,组件是没有状态的。来看看使用Hooks的方式:

import { useState, useEffect } from 'react';
import {Keyboard
} from 'react-native';export default function keyboard() {const [keyboardStatus, setKeyboardStatus] = useState(false);Keyboard.addListener("keyboardDidShow",this.keyboardDidShowHandler);useEffect(()=> {return ()=> Keyboard.removeListener("keyboardDidShow",()=> setKeyboardStatus(true));}, [false]);return (<Text>当前键盘状态:{keyboardStatus}</Text>)
}复制代码

上述代码中将关于键盘的业务逻辑剥离到了函数中,称之为 函数组件。当我们在其他Component中使用时,只需要导入进来即可。在函数组件中,我们使用到了 useState、useEffect,它们作为Hooks中提供的Api,起到了什么作用呢?

Hooks Api

官方提供了 hooks 的三个关键的Api,分别是 State Hooks 、 Effect Hooks 、Context Hooks、 Custom Hooks(自定义hooks)。

useState

useState 这个方法可以为函数组件带来 local state,它接收一个用于初始 state 的值,返回一对变量

// 等价于const keyboardStatus= useState(0)[0];const setKeyboardStatus= useState(0)[1];复制代码

理解起来比较简单,其实就是定义 state 状态值,以及修改该 state 状态值的行为函数。

useEffect

useEffect 可以简单的理解为替代如下生命周期:

componentDidMount、componentDidUpdate、componentWillUnmount

useEffect 的代码既会在第一次初始化时(componentDidMount)执行,也会在后续每次触发 render 渲染时(componentDidUpdate)执行,返回值在组件注销时(componentWillUnmount)执行。结合上面的例子:

useEffect(()=> {// return 将会在组件注销时调用return ()=> Keyboard.removeListener("keyboardDidShow",()=> setKeyboardStatus(true));
}, [false]);复制代码

useEffect 的第二个参数,作为性能优化的设置,决定是否执行里面的操作来避免一些不必要的性能损失。只要第二个参数数组中的成员的值没有改变,就会跳过此次执行。如果传入一个空数组 [ ],那么只会在组件 mount 和 unmount 时期执行。

Context Hooks

React 16.3 版本中发布了全新的Context API。目的为了解决子组件嵌套层级过深,父组件的属性难以传达的问题。使用方式不算复杂,首先要利用 Context API创建一个数据提供者(Provider)和数据消费者(Consumer)。(说到这里有点像Java多线程并发的例子)在提供者所在的地方存入数据,在消费者所在的地方取出数据。简单看下 Context 使用方式: (1)创建上下文环境

// 创建 Contextimport React from 'react';const DataContext = React.createContext({    name: '',    age: 23});
export default DataContext;
复制代码

(2)定义数据提供者 Provider

export default class App extends Component {render() {return (<DataContext.Provider value={{ name: 'Songlcy', age: 27 }}><CustomComponent /></DataContext.Provider>);}
}复制代码

(3)定义数据消费者 Consumer

export default class CustomComponent extends Component {render() {return (<DataContext.Consumer>{context => (<Text>我的名字:{context.name}, 我的年龄:{context.age}</Text>)}</DataContext.Consumer>)}
}复制代码

当组件嵌套层次很深的情况下,Context 的优势就会更为明显。 “诶,醒醒!”..... 说了这么多,继续回到Hooks。上面代码中,从 Context — Provider — Consumer 获取到数据,整个取值过程还是比较繁琐的。当我们要从多个 Consumer 中取值的时候,还要进行函数嵌套,更加麻烦。 useContext 是对 Context API 的简化。来看看简化后的样子:

const { name, age } = useContext(DataContext);复制代码

我靠!这就完了?” 是的,取值过程就是这么简单,就是这么任性。再来10个 Consumer 又如何!

Custom Hooks

Custom Hooks 即自定义Hooks行为方式,本身并不是Api。核心概念就是将逻辑提取出来封装到函数中,具体实现就是通过一个函数封装跟状态数据(State)有关的逻辑,将这些逻辑从组件中抽取出来。在这个函数中我们可以使用其他的 Hooks,也可以单独进行测试。修改上面的例子:

export default function useKeyboardStatus() {const [keyboardStatus, setKeyboardStatus] = useState(false);Keyboard.addListener("keyboardDidShow",this.keyboardDidShowHandler);useEffect(()=> {return ()=> Keyboard.removeListener("keyboardDidShow",()=> setKeyboardStatus(true));},[]);return keyboardStatus;
}复制代码

代码几乎相同,唯一区别是函数名称用了 use* 前缀,这里需要遵循一个约定,命名要用 use*

Hooks 工作原理

“神马?Hooks 其实就是一个数组!”

回忆下最初我们使用 useState 时的方式:

const [keyboardStatus, setKeyboardStatus] = useState(false);复制代码

其实从这句代码我们也能猜出大致的实现思想:

使用一个类似于 setter 的函数作为hook函数中的第二个数组项返回,而 setter 将控制由hook管理的状态(State),状态由第一个数组项返回。

我们可以理解成有两个数组,分别存放 state、setState对应的方法。 当useState()第一次运行时,将setter函数推送到setter数组,状态推送到state数组。每个setter都有一个对它的光标位置的引用,因此通过触发对任何setter的调用,它将改变状态数组中该位置的状态值。说白了就是有个索引,setter方法根据索引修改对应的状态数据值。来看看伪代码的实现方式:

let state = []; // 存放state状态数据
let setter = []; // 存放 setXXX方法
let initial = true; // 是否是第一次运行
let index = 0;useState(initVal) {if (initial) {state.push(initVal);setter.push(createSetter(index));initial = false;}const setter = setter[index];const value = state[index];index++;return [value, setter];
}createSetter(index) {return function setterWithIndex(newVal) {state[index] = newVal;};
}复制代码

具体的源码实现,感兴趣的大家可以去看看。不过不建议每步都弄懂,了解了实现思想就可以了。

总结

状态和相关的处理逻辑可以按照功能进行划分,不必散落在各个生命周期中,大大降低了开发和维护的难度。除了这几个hooks还有其他额外的hooks:Hooks API Reference

最后推荐两个个很牛逼的库:

react-use: 封装了各种 Hooks。

eslint-plugin-react-hooks: Hooks ESLint 插件

一个老外写的很不错的 React Native Hooks 文章:React Hooks Basics— Building a React Native App with React Hooks

转载于:https://juejin.im/post/5ce749b8e51d454f73356cb6

在 React Native 中使用 Hooks相关推荐

  1. 如何在React Native中使用react-navigation 5处理导航

    React-navigation is the navigation library that comes to my mind when we talk about navigation in Re ...

  2. 理解 React Native 中的 AJAX 请求

    曾经,大多数 Web 应用程序通过用户操作刷新整个网页以与 Web 服务器通信. 后来,AJAX(异步 JavaScript 和 XML)概念通过提供一种在后台与 Web 服务器通信的方式使 Web ...

  3. 我在React Native中构建时获得的经验教训

    by Amanda Bullington 通过阿曼达·布林顿(Amanda Bullington) 我在React Native中构建时获得的经验教训 (Lessons I learned while ...

  4. 如何在React Native中写一个自定义模块

    前言 在 React Native 项目中可以看到 node_modules 文件夹,这是存放 node 模块的地方,Node.js 的包管理器 npm 是全球最大的开源库生态系统.提到npm,一般指 ...

  5. 如何在React Native中记录日志?

    本文翻译自:How to do logging in React Native? 如何为Web开发时在React Native中记录变量,例如使用console.log ? #1楼 参考:https: ...

  6. react native中一次错误排查 Error:Error: Duplicate resources

    最近一直在使用react native中,遇到了很多的坑,同时也学习到了一些移动端的开发经验. 今天在做一个打包的测试时,遇到了一个问题,打包过程中报错"Error:Error: Dupli ...

  7. Android之React Native 中组件的生命周期

    React Native 中组件的生命周期 概述 就像 Android 开发中的 View 一样,React Native(RN) 中的组件也有生命周期(Lifecycle).所谓生命周期,就是一个对 ...

  8. 如何在 React Native 中写一个自定义模块

    前言 在 React Native 项目中可以看到 node_modules 文件夹,这是存放 node 模块的地方,Node.js 的包管理器 npm 是全球最大的开源库生态系统.提到npm,一般指 ...

  9. 了解React Native中的不同JavaScript环境

    by Khoa Pham 通过Khoa Pham 了解React Native中的不同JavaScript环境 (Get to know different JavaScript environmen ...

  10. 如何在React Native中使用Redux Saga监视网络更改

    by Pritish Vaidya 通过Pritish Vaidya 如何在React Native中使用Redux Saga监视网络更改 (How to monitor network change ...

最新文章

  1. 路由器snmp配置_基于keepalived配置数据库主从实现高可用
  2. golang怎么给空结构体赋值
  3. Rabbit的Windows安装
  4. Linux中的history命令
  5. PHP生成Mysql数据字典
  6. 高露洁、悦诗风吟、Benefit,618大促的数字化难题都是如何解决的?
  7. Linux使用LVM逻辑卷 给分区扩容 不重启 热扩容
  8. linux-LINUX试题
  9. “电”亮数字生活,阿里云助力南方电网智能调度
  10. cad快速选择命令快捷键_CAD快捷键命令大全
  11. Java基础Day05
  12. 永中java的窗口_永中國際 Office
  13. qt编写的mask遮罩的半透明涂抹功能
  14. 中古消费热潮下,爆爆奢漫步二奢直播新时代
  15. Compensating-Transaction模式
  16. 浅析STM32H7 FDCAN(一)
  17. Velodyne 32E pcap包GPS时间戳解析
  18. 制作一个带rEFInd引导菜单的WinPE启动光盘
  19. 面经02-深信服面试
  20. 一个能测试怀孕的软件,四个方法测怀孕 准爆了

热门文章

  1. android wifi操作,热点创建
  2. 小游戏策划案例精选_小游戏策划方案
  3. python---用python实现选择排序
  4. chown: `mysql#039;: invalid user_centos无法正常启动,报chown: invalid user:'root:root'
  5. java空指针找不到,跪空指针异常,所有的传入参数都判断了非空,实在找不到哪里没有赋值了...
  6. android改变textview文字,如何在Android TextView中更改文本
  7. 性能可靠服务器虚拟化,服务器虚拟化分析
  8. python warning_warning最新:Python 忽略warning的输出方法_爱安网 LoveAn.com
  9. mysql on 子句_ON子句中的MySQL未知列
  10. Python 爬虫经常需要睡眠防止被封IP time sleep