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 Indeximport 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