Hook 是 React 16.8的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
1. useState
使用这个 hook 可以方便我们在函数组件中操作 state,下面例子演示和如何使用 useState 来操作基本类型、数组、对象、函数不同类型的 state。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import React , { useState } from 'react' ;const Index = ( ) => { const [count,setCount] = useState (0 ) const [obj,setObj] = useState ({name :'Lisi' }) const [arr,setArr] = useState ([1 ,2 ,3 ]) const [func,setFunc] = useState (()=> { return {name : 'func' } }) return ( <div > <p > count: {count}</p > <button onClick ={() => setCount(count+1)}>+1</button > <p > name: {obj.name}</p > <button onClick ={() => setObj({name:'Mazi'})}>changeObjectName1</button > <button onClick ={() => setObj({ ...obj, name: 'Loali' })}>changeObjectName2</button > <button onClick ={() => setObj(Object.assign({},obj,{name:'Lili'}))}>changeObjectName3</button > <p > Array: {arr}</p > <button onClick ={() => setArr(()=>{ arr.push(4) return [...arr] })}>changeArray</button > <p > func: {func.name}</p > <button onClick ={() => setFunc({name:'function'})}>changeFuncion</button > </div > ) } export default Index
注意的是,在设置 state 时候,对于数组、对象的设置应该返回一个新的数组或者对象,而对于函数设置,相当于设置函数的返回值类型。
2. useEffect
此方法用于执行相应的副作用(DOM操作、数据请求、组件更新),相当于 class 组件中的 componentDidMount、componentDidUpdate、componentWillUnmount,它采用闭包的形式,可以获取 state 和 props 在组件内部执行,在组件挂载之后执行可以达到无阻塞更新的目的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import React , { useState, useEffect } from 'react' ;const Index = ( ) => { const [count,setCount]=useState (0 ) useEffect (()=> { console .log (count) },[count]) useEffect (()=> { console .log (count) },[]) return ( <div > <p > count: {count}</p > <button onClick ={() => setCount(count+1)}>+1</button > </div > ) } export default Index
3. useContext
此方法接收一个 context 对象(React.createContext的返回值)并返回该 context 的当前值。当前的context值由上层组件中距离当前组件最近的<MyContext.Provider>的value prop决定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import React , { useState, useContext, createContext } from 'react' ;const MyContext = createContext ();const ChildContext = ( )=> { let count = useContext (MyContext ) return (<h3 > {count}</h3 > ) } const Index = ( ) => { const [count, setCount] = useState (0 ) return ( <div > <MyContext.Provider value ={count} > <ChildContext > </ChildContext > </MyContext.Provider > <button onClick ={() => { setCount(count + 1) }}>button</button > </div > ) } export default Index
4. useRef
此方法返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import React , { useState, useContext, createContext } from 'react' ;const TextInputWithFocusButton = ( ) => { const inputEl = useRef (null ) const onButtonClick = ( ) => { inputEl.current .focus (); } return ( <> <input ref ={inputEl} type ="text" /> <button onClick ={onButtonClick} > 获取Input焦点</button > </> ); } export default Index
我们可使用 useRef 可以很方便的保存任何可变值;还可以结合 ref 对象以<div ref={myRef} />形式传入组件,则无论该节点如何改变,React 都会将 ref 对象的 .current 属性设置为相应的 DOM 节点
5. useMemo
此方法利用 js 的 Memoization 缓存机制把计算的值以 key 的形式保存在内存中,当下次在需要用到这个值的时候直接从内存中读取,而不需要在进行计算,以到达优化运行速度的目的,使用方法和 useEffect 差不多,该方法在渲染期间执行,请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。
我们可以对比一下该方法和 useEffect 的执行顺序,我们可以看到先执行 useMemo 函数,再执行 useEffect 函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import React , { useState, useEffect, useMemo } from 'react' ;const Index = ( ) => { const [count, setCount] = useState (0 ) useEffect (()=> { console .log ('effect' ) }) useMemo (()=> { console .log ('Memo' ) }) return ( <div > <p > count:{count}</p > <button onClick ={() => { setCount(count + 1) console.log(count) }}>buttonCount</button > </div > ) } export default Index
如果我们给它的第二个参数传入空数组,此时不更新任何状态,但要注意的是,state 的数据是改变的,只是没有更新到页面上而已,这就会导致下面代码运行的问题,当我们监听多个状态时,不监听的那个状态被改变,再触发监听的状态改变的时候,不监听的那个状态值也一并会从缓存中取出来更新到页面上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import React , { useState, useMemo } from 'react' ;const Index = ( ) => { const [count, setCount] = useState (0 ) const [num, setNum] = useState (0 ) let res = useMemo (()=> { return { count, num } },[count]) return ( <div > <p > count:{res.count}; num:{res.num}</p > <button onClick ={() => { setCount(count + 1) console.log(count) }}>buttonCount</button > <button onClick ={() => { setNum(num + 1) console.log(num) }}>buttonNum</button > </div > ) } export default Index
当遇到需要在父子使用对应的缓存机制的时候,只需要在子组件中对父组件传过来的 props 值进行 useMemo 控制就行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import React , { useState, useMemo } from 'react' ;import ChildMome from './ChildMome.js' ;const Index = ( ) => { const [count, setCount] = useState (0 ) const [num, setNum] = useState (0 ) let res = useMemo (()=> { return { count, num } },[count, num]) return ( <div > <ChildMome c ={count} n ={num} /> <p > count:{res.count}; num:{res.num}</p > <button onClick ={() => { setCount(count + 1) console.log(count) }}>buttonCount</button > <button onClick ={() => { setNum(num + 1) console.log(num) }}>buttonNum</button > </div > ) } export default Index import React , { useMemo } from 'react' ;const ChildMome = ({c, n} ) => { let childRes = useMemo (()=> { return { c, n } },[c, n]) return ( <div > <h3 > Child Memonization</h3 > <p > c:{childRes.c} ;n:{childRes.n}</p > </div > ) } export default ChildMome
6. useCallback
和 useMemo 功能类似,局部用到缓存机制,但与其不同的是,这个方法用来缓存一个函数,该函数可以通过方法的返回值来执行,通常用来控制组件什么时候更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import React , { useState, useCallback } from 'react' ;const Index = ( ) => { const [count, setCount] = useState (0 ) let callback = useCallback (()=> { console .log (count) return count }) return ( <div > <p > count:{count};</p > <h4 > {callback()}</h4 > <button onClick ={() => { setCount(count + 1) console.log(count) }}>buttonCount</button > </div > ) } export default Index