State 引入方式 1 import React, {useState, useReducer} from 'react'
useState声明方式 1 const [state, setState] = useState(initialState);
useState返回后的第一个值将始终是更新后的最新的state
setState修改状态方法 1 2 3 4 5 6 7 8 9 10 11 function Counter ({initialCount} ) { const [count, setCount] = useState(initialCount); return ( <> Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</ button> <button onClick={() => setCount(prevCount => prevCount + 1 )}>+</button> </ > ); }
useReducer声明方式 1 const [state, dispatch] = useReducer(reducer, initialArg, init);
e.g.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const initialState = {count : 0 };function reducer (state, action ) { switch (action.type) { case 'increment' : return {count : state.count + 1 }; case 'decrement' : return {count : state.count - 1 }; default : throw new Error (); } } function Counter ( ) { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({type : 'decrement' })}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</ button> </> ); }
Effect useEffect 1 2 3 4 5 useEffect(() => { console .log('effect invoked' ) return () => console .log('effect detected' ) }, [count] );
useLayoutEffect
函数签名与useEffect相同
会在任何state更新后,计算新DOM节点数,还未更新到真实DOM页面时执行
执行顺序
1 2 3 4 5 6 Layout effect invoked b_old.js:65 effect invoked b_old.js:74 layout effect detected b_old.js:73 Layout effect invoked b_old.js:66 effect detected b_old.js:65 effect invoked
Context
Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.
React.createContext() && useContext() 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 const themes = { light: { foreground: "#000000" , background: "#eeeeee" }, dark: { foreground: "#ffffff" , background: "#222222" } }; const ThemeContext = React.createContext(themes.light);function App ( ) { return ( <ThemeContext.Provider value={themes.dark}> <Toolbar /> </ThemeContext.Provider> ); } function Toolbar(props) { return ( <div> <ThemedButton / > </div> ); } function ThemedButton() { const theme = useContext(ThemeContext); return ( <button style={{ background: theme.background, color: theme.foreground }}> I am styled by theme context! </ button> ); }
useRef
获取某一个DOM节点,或组件的实例
ref过去在functional component没有,因为无this,无法挂载
返回同一个对象,不会生成新的对象
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 function MyCountFunc ( ) { const [count, dispatchCount] = useReducer(countReducer, 0 ) const [name, setName] = useState('John' ) const inputRef = useRef() useEffect(() => { console .log(inputRef) return () =>console .log('effect detected' ) }, [count]) useLayoutEffect(() => { console .log('Layout effect invoked' ) return () =>console .log('layout effect detected' ) }, [count]) return ( <div> <input ref={inputRef} value={name} onChange={(e)=>setName(e.target.value)}/> <button onClick={()=>dispatchCount({type : 'add' })}>{count}</button> <p>{Context}</ p> </div> ) }
Hooks Render Optimization
memo
useMemo: 普遍用于对函数中参数的优化
useCallback: 普遍用于对函数的优化
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 function MyCountFunc ( ) { const [count, dispatchCount] = useReducer(countReducer, 0 ); const [name, setName] = useState('John' ) const countRef = useRef() countRef.current = count const config = useMemo(() => ({ text: `count is ${count} ` , color: count>3 ? 'red' : 'blue' }) , [count]) const handleButtonClick = useCallback(() => dispatchCount({type : 'add' }), [dispatchCount]) const handleAlertButtonClick = function ( ) { setTimeout(() => { alert(countRef.current) }, 2000 ) } return ( <div> <input value={name} onChange={e=>setName(e.target.value)}/> <Child config = {config} onButtonClick={handleButtonClick} /> <button onClick={handleAlertButtonClick}>alert count</button> </ div> ) } const Child = memo( function Child ( {onButtonClick, config} ) { console .log('child render' ); return ( <button onClick={onButtonClick} style={{color : config.color}}> {config.text} </button> ) } )
Closure Trap 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 function MyCountFunc ( ) { const [count, dispatchCount] = useReducer(countReducer, 0 ); const [name, setName] = useState('John' ) const countRef = useRef() countRef.current = count const config = useMemo(() => ({ text: `count is ${count} ` , color: count>3 ? 'red' : 'blue' }) , [count]) const handleButtonClick = useCallback(() => dispatchCount({type : 'add' }), [dispatchCount]) const handleAlertButtonClick = function ( ) { setTimeout(() => { alert(countRef.current) }, 2000 ) } return ( <div> <input value={name} onChange={e=>setName(e.target.value)}/> <Child config = {config} onButtonClick={handleButtonClick} /> <button onClick={handleAlertButtonClick}>alert count</button> </ div> ) }
在调用setTimeout函数时,如果将count作为参数,则输出的是调用该函数时的count对象。如果想获取即时的值,则使用useRef