内容简介:React官方在 2018 ReactConf 大会上宣布 React v16.7.0-alpha(内测) 将引入 Hooks。什么是Hooks,我们来了解一下。在平时开发过程中,我们一般都会遇到如下问题:上面说起来比较抽象,接下来我们以 键盘Api
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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- RecyclerView使用指南(一)—— 基本使用
- 如何使用Meteorjs使用URL参数
- 使用 defer 还是不使用 defer?
- 使用 Typescript 加强 Vuex 使用体验
- [译] 何时使用 Rust?何时使用 Go?
- UDP协议的正确使用场合(谨慎使用)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。