1、什么是hook?
react hook是react 16.8推出的办法,可能让函数式组件像类式组件一样领有state、ref、生命周期等属性。
2、为什么要呈现hook?
函数式组件是全局当中一个一般函数,在非严格模式下this指向window,然而react外部开启了严格模式,此时this指向undefined,无奈像类式组件一样应用state、ref,函数式组件定义的变量都是部分的,当组件进行更新时会从新定义,也无奈存储,所以在hook呈现之前,函数式组件有很大的局限性,通常状况下都会应用类式组件来进行代码的编写。
3、有哪些罕用的hook?
(1) useState
使函数式组件也能保留状态的一个hook,这个hook的入参是状态的初始值,返回值是一个数组,数组里第一个参数为状态的值,第二个参数为批改状态的办法。
// 初始化const [ count, setCount ] = useState(0)// 更新setCount(count+1)
(2) useEffect
函数式组件用来模仿生命周期的hook,能够模仿组件挂载实现、更新实现、行将卸载三个阶段,即componentDidMount、componentDidUpdate、componentWillUnmount。
useEffect的一个参数为函数,示意组件挂载、更新时执行的内容,在函数里再返回一个函数,示意组件行将卸载时调用的函数。
第二个参数为可选项,可传入数组,数组里能够为空,示意不依赖任何状态的变动,即只在组件行将挂载时执行,后续任何状态产生了变动,都不调用此hook。数组里也能够定义一或多个状态,示意每次该状态变动时,都会执行此hook。
useEffect(()=>{ // 这样模仿的是 componentDidMount}, [])useEffect(()=>{ // 这样模仿的是componentDidMount 以及当count发生变化时执行componentDidUpdate}, [count])useEffect(()=>{ return ()=>{ // 这样模仿的是 componentWillUnmount }}, [])
(3) useContext
在没有hook之前,咱们通常都会通过 xxxContext.Provider 和 xxxContext.Consumer 的形式来传递和获取context的值,应用hook之后,传递context的形式不变,但子元素获取context的形式变得更加的简洁。
// 以前的定义形式const CountContext = React.createContext() <CountContext.Provider value={{ count: 10 }}> <...自定义的组件> </CountContext.Provider>// 子元素<CountContext.Consumer> { value => { console.log(value.count) }} //10</CountContext.Consumer>//应用context的获取形式const countObj = useContext(CountContext)console.log(countObj.count) // 10
(4) useRef
useRef和类式组件中createRef用法比拟相似,返回一个ref对象,这个对象在函数的整个生命周期都不变,依据这个个性,有两种比拟常见的用法。
① 用于dom元素或者组件上,通过current属性能够获取到dom元素或者类式组件的实例对象。须要留神的是,无论是useRef还是createRef或者是回调模式、字符串模式的ref,都是不能间接给函数式组件定义的,因为函数式组件的this指向undefined,没有实例对象,只能通过forwardRef定义到函数式组件中的某个dom元素。
// 这样就将传递给函数式组件的ref绑定在了函数式组件外部的input标签上import React, { useRef, forwardRef } from 'react'// 应用函数表达式的形式定义了一个函数式组件const InputCom = forwardRef((props, ref) => { return <input ref={ref}/> })export default function refDemo(){ const comRef = useRef() return(<div> <InputCom ref={comRef}/> </div>) }
② 保留一个数据,该数据如果不手动批改,它在整个生命周期中都不变
const [ count, setCount ] = useState(0)const prevCount = useState(count)// 当count发生变化时,组件更新,对count的前一次数据进行保留useEffect(()=>{ prevCount.current = count}, [count])
(5) useReducer
useReducer相当于是useState的升级版,作用与useState相似,都是用来保留状态,但它的不同点在于能够定义一个reducer的纯函数,来解决简单数据。
// 定义一个解决数据的reducer纯函数function reducer(prevState, action){ switch(action.type){ case 'increment': return {...prevState, count: prevState.count + 1 } case 'decrement': return {...prevState, count: prevState.count - 1 } default: return prevState }}// 初始化状态const [ count, dispatch ] = useReducer(reducer, { count: 0 })// 批改状态,此时的批改须要派发一个action,让传入的reducer函数进行解决dispatch({ type: 'increment' })
(6) useCallback
函数式组件中,每一次更新状态,自定义的函数都要进行从新的申明和定义,如果函数作为props传递给子组件,会造成子组件不必要的从新渲染,有时候子组件并没有应用到父组件发生变化的状态,此时能够应用useCallback来进行性能优化,它会为函数返回一个记忆的值,如果依赖的状态没有发生变化,那么则不会从新创立该函数,也就不会造成子组件不必要的从新渲染。
import React, { useState, useCallback, memo } from 'react'const AddBtn = memo((props)=>{ // 应用函数表达式的形式定义了一个函数式组件 return<button onClick={props.increment}>+1</button>})export default function CallBackPerformance(){ const [ count, setCount ] = useState(0) const [ show, setShow ] = useState(true) const increment1 = () => { console.log('increment1被调用了') setCount(count+1) } const increment2 = useCallback(()=>{ // 应用了useCallback来优化的函数 console.log('increment2被调用了') setCount(count+1) },[count]) return(<div> <div>以后计数:{count}</div> <AddBtn increment={increment1} name="1"/> <AddBtn increment={increment2} name="2"/> <button onClick={e => setShow(!show)}>切换show</button> </div>)}// 当show这个状态发生变化时,子组件increment1会从新渲染,increment2不会从新渲染
(7) useMemo
useMemo也是返回一个记忆的值,如果依赖的内容没有产生扭转的话,这个值也不会发生变化,useMemo与useCallback的不同点在于useMemo须要在传入的函数里须要return 一个值,这个值能够是对象、函数,格局如下。
useMemo(()=>{ return { count }}, [count])// 应用useCallback时 const increment2 = useCallback(()=>{ setCount(count+1)},[count])// 应用useMemo模仿useCallbackconst increment2 = useCallback(()=>{ return ()=>{ setCount(count+1) }},[count])// useMemo的利用场景,当要进行一些简单的计算时,//计算的值没有发生变化,并不需要每一次更新都从新计算import React, { useState, useMemo } from 'react'const calculateNum = (count) => { console.log('total从新计算了') let total = 0 for(let i = 0; i <= count; i++){ total += i } return total}export default function ComplexUseMemo(){ const [ count, setCount ] = useState(10) const [ show, setShow ] = useState(true) const total = useMemo(()=>{ return calculateNum(count) }, [count]) return(<div> <div>{total}</div> <button onClick={e=>setCount(count+1)}>+1</button> <button onClick={e=>setShow(!show)}>切换show</button> </div>)}
(8) useImperativeHandle
这个是与forwardRef配合来应用的,当咱们对函数式组件应用forwardRef将ref指定了dom元素之后,那就父组件就能够任意的操作指定的dom元素,应用useImperativeHandle就是为了管制这样的一种行为,指定父元素可操作的子元素的办法。
import React, { useRef, useImperativeHandle, forwardRef } from 'react'const InputComp = forwardRef((props, ref)=>{ const childInputRef = useRef() useImperativeHandle(ref, ()=>({ focus: ()=>{ childInputRef.current.focus() } }), [childInputRef.current]) return<input ref={childInputRef}></input>})export default function ImperativeHookDemo() { const inputRef = useRef() return(<div> <InputComp ref={inputRef}/> <button onClick={e=>inputRef.current.focus()}>聚焦</button> </div>)}
(9) useLayoutEffect
这个办法与useEffect相似,只是执行的程序稍有不同,useEffect是在组件渲染绘制到屏幕上之后,useLayoutEffect是render和绘制到屏幕之间。
4、如何自定义hook?
hook只能定义在函数式组件中,不能在一般函数中应用,如果咱们想要应用到下面的hook来封装一些办法供很多个组件调用,这时候就须要自定义hook,自定义hook的命名就是在函数名前加 use,函数名由 saveInfo 改为 useSaveInfo 即可。