紫棋的一句想要我帮你唱hook吗?让hook红了一把!
作为一个开发,尤为是前端开发,在电视上听到这个词仍是有点小兴奋的(虽然彼hook非此hook)。玩够这个梗,是否是也要了解一下本身的react hook?html
使用react有一段时间了,组件化编程也早已成为习惯。经常使用的两种编写组件的方式就是就是class组件和函数组件。
class组件:经过继承React.Component来构建组件,虽然提供了state状态和完备的生命周期函数,可是也有不少不方便的地方。前端
componentDidMount() { document.title = `You clicked ${this.state.count} times`; ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } componentWillUnmount() { ChatAPI.unsubscribeFromFriendStatus( this.props.friend.id, this.handleStatusChange ); }
函数组件:经过函数直接书写的组件,看起来是简洁了许多,可是不能使用state,也没有生命周期函数、更不能使用react的一些其余特性。只能经过父组件传递进来,任人鱼肉,只能惨淡的沦为展现组件。react
那么,函数组件就只能沦为展现的花瓶吗?能结合class组件能使用react特性的优势和函数组件简洁优雅的特性吗?好在react hook来了,函数组件的春天来了。编程
React 16.8 新增Hook特性。它可让你在不编写 class 的状况下使用 state 以及其余的 React 特性。api
是否是感受hook挺神奇的?hook是什么?怎么使用?不用惊讶,hook就是一个钩子函数,钩着react的一些api供给函数组件使用。数组
先看一个官方的useState例子:函数
import React, { useState } from 'react'; function Example() { // 声明一个叫 “count” 的 state 变量。 const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
引入一个hook(useState函数),传入一个初始值0,而后函数返回了一个数组,经过数组的结构赋值,取得state, count = 0和修改count值的函数setCount。当button发生点击事件时,触发setCount函数,而且传入新的count值完成对state值的修改,并展现到页面上。代码转换成class组件以下:组件化
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); } }
看明白了吗?性能
Hook 是一些可让你在函数组件里“钩入” React state 及生命周期等(context、refs)特性的函数。
React 内置了一些像 useState 这样的 Hook。你也能够建立你本身的 Hook 来复用不一样组件之间的状态逻辑。优化
一个组件中可使用多个hook,每一个hook会独立存在,内部状态不会共用。每次更新组件时,hook会按顺序从上到下执行。
function Form() { // 1. Use the name state variable const [name, setName] = useState('Mary'); // 2. Use the age state variable const [age, setAge] = useState('12'); // 3. Use the surname state variable const [surname, setSurname] = useState('Poppins'); // ... } 第一次执行结果: 设置状态name Mary; 设置状态age 12; 设置状态surname Poppins; 第二次执行结果: 设置状态name Mary; 设置状态age 12; 设置状态surname Poppins;
那么,react是如何知道哪一个 state 对应 哪个useState?很简单, React 靠的是 Hook 调用的顺序。
react hook 使用,要遵循下面两个规则:
只在最顶层使用 Hook
react依靠hook调用顺序来对应state,因此调用顺序不能变。
function Form() { // 1. Use the name state variable const [name, setName] = useState('Mary'); // 2. Use the age state variable if(!name){ const [age, setAge] = useState('12'); } // 3. Use the surname state variable const [surname, setSurname] = useState('Poppins'); // ... } 第一次执行结果: 设置状态name Mary; 设置状态age 12; 设置状态surname Poppins; 第二次执行结果: 设置状态name Mary; 设置状态age 12;//判断后会被忽略 设置状态surname 12;//此时设置为12,与需求已不符
也就是说只能在顶层使用hook。
在class组件中是没法使用hook函数的,只能在函数组件中使用。
在自定义hook中也可使用。
上面使用的useState Hook是一种可让你在函数组件内使用state的内置hook,除此以外react还提供了许多内置hook:
基础 Hook
useState useEffect useContext
额外的 Hook
useReducer useCallback useMemo useRef useImperativeHandle useLayoutEffect useDebugValue
下面看一下 useState、useEffect两个经常使用的hook,其余的使用频率较低,使用方法也都基本一致其余hook
咱们想在函数组件里加入state,点击+,完成数字累加功能:
import React , {useState} from 'react' function Item(){ const [count,setCount] = useState({name:'tom',age:11}) return <> {`${count.name}已经${count.age}岁了`} <div onClick={()=>{setCount({name:'janny',age:count.age+1})}}>+</div> </> } export default Item;
从上面代码能够看出来,useState hook提供了在函数组件里使用state的能力。十分简洁。
首先,经过react引入useState钩子函数;
接着,在函数组件内调用useState,并传入state的初始值,能够是个值,也能够是对象。useState函数的返回值个数组,这里经过数组的结构赋值取得count(变量名能够随意定义)和修改count的方法setCount(函数名也能够随意定义)。
取值state:能够经过变量count直接取值
修改state:经过给setCount函数传递值来修改
是否是很简单?之后终于能够在函数组件里使用state了,拥有控制本身主权的能力了!除了使用state,还有一种场景也是咱们常常碰到的,那就是函数组件没有生命周期,一些反作用的操做无法完成!别着急,react又内置了极好的effect hook!
当咱们点击+时,不只想完成数字累加,并且想要在title里显示咱们的修改,若是在class组件里,咱们能够在componentDidMount生命周期里经过setState完成,可是函数组件里无法完成,即便如今有useState Hook也没有生命周期函数,无法完成!那就在来一个hook吧,引入effect Hook。
useEffect hook不只提供了生命周期,并且useEffect Hook能够看作componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。也就是在这一个钩子函数里能够完成不少事!
import React , {useState,useEffect} from 'react' function Item(){ const [count,setCount] = useState({name:'tom',age:11}) useEffect(()=>{ document.title = count.age }) return <> {`${count.name}已经${count.age}岁了`} <div onClick={()=>{setCount({name:'tom',age:count.age+1})}}>+</div> </> } export default Item;
好好感觉一下!useEffect就像是一个生命周期函数。这个功能在 class 组件中,须要在两个生命周期函数(componentDidMount、componentDidUpdate)中编写重复的代码。在函数useEffect内进行反作用操做就好了。由于每次挂载、更新后useEffect都会执行。
在 React 组件中有两种常见反作用操做:须要清除的和不须要清除的。上面这种就是不须要清除的反作用。
假如,咱们以为只用手指点击太累了,想弄个计时器帮咱们执行。
let timer = null; function Item(){ const [count,setCount] = useState({name:'tom',age:11}) useEffect(()=>{ clearInterval(timer);//清除 timer = setInterval(()=>{ console.log(1) setCount({name:'tom',age:count.age+1}) },1000) document.title = count.age }) return <> {`${count.name}已经${count.age}岁了`} <div onClick={()=>{setCount({name:'tom',age:count.age+1})}}>+</div> </> }
这样就很棒,换上自动挡就很舒服!可是当咱们切换路由,移除这个组件后,出去看看风景回来!发现定时器没有停,本身玩的很嗨...哎呦喂!
这就是须要清除的反作用,class组件里咱们能够经过在componentWillUnmount生命周期函数里清除,因此这里咱们也要清除这些须要清除的反作用!
function Item(){ const [count,setCount] = useState({name:'tom',age:11}) useEffect(()=>{ clearInterval(timer) timer = setInterval(()=>{ console.log(1) setCount({name:'tom',age:count.age+1}) },1000) document.title = count.age; return function(){//add clearInterval(timer) } }) return <> {`${count.name}已经${count.age}岁了`} <div onClick={()=>{setCount({name:'tom',age:count.age+1})}}>+</div> </> }
useEffect清除反作用的方式也很简单,return 一个函数,在函数里清除就能够了。
上面说了useEffect hook在每次挂载、更新后都会执行。这不就是跟setState在性能方面形成的问题同样严重吗?class组件 经过在 componentDidUpdate 进行优化,useEffect怎么优化!
能够经过useEffect函数的第二个参数进行优化。
1.传递空数组,表示函数只执行一次!这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,因此它永远都不须要重复执行。effect 内部的 props 和 state 就会一直拥有其初始值。更接近你们熟悉的 componentDidMount 。
useEffect(()=>{ ... },[])
2.传递包含state的数组
会经过===比较count的前一次渲染的值与这一次要渲染的值,若是全等则React 会跳过这个 effect,这就实现了性能的优化。必定要将全部须要比较的state都放进数组,不然将不会进行比较,也就会直接跳过!
useEffect(()=>{ ... },[count])
react内置了多hook,咱们也能够对这些hook进行业务方面的封装,写出属于本身的hook!咱们将上面的叠加计时封装成一个自定义hook:数字每秒叠加,而且修改显示在title上。
import React , {useState,useEffect} from 'react' function useTitle(){//自定义hook let timer = null; const [count,setCount] = useState({name:'tom',age:11}) useEffect(()=>{ clearInterval(timer) timer = setInterval(()=>{ setCount({name:'tom',age:count.age+1}) },1000) document.title = count.age; return function(){ clearInterval(timer) } },[count]) return count;//返回一个对象 } function Item(){//自定义hook使用 const count = useTitle() return <> {`${count.name}已经${count.age}岁了`} </> }
将一些公共代码提取出来,最后返回须要在页面显示的数据就成了
到这里,react hook已经有所了解了吧!就是让函数组件可以像class组件同样自由自在的使用react的特性的函数。能够不影响以前任何业务,在项目中彻底可选、100%向后兼容的react新特性!快在项目中用起来吧!
若有不妥!欢迎指正