[React] 函数组件
创建一个函数组件
1 2 3 4 5 6 7 8 9
| const Hello = (props) => { return <div>{props.message}</div> }
const Hello = props => <div>{props.message}</div>
function Hello(props){ return <div>{props.message}</div> }
|
Hooks
useState
-
setN
一定会触发重新渲染
-
数据会被存入新的n
1 2 3 4 5 6 7 8 9 10 11
| function App() { const [n, setN] = React.useState(0); return ( <div className="App"> <p>{n}</p> <p> <button onClick={() => setN(n + 1)}>+1</button> </p> </div> ); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function App() { const [n, setN] = useState(0) const onClick = ()=>{ setN(n+1) setN(n+1) } return ( <div className="App"> <h1>n: {n}</h1> <button onClick={onClick}>+2</button> </div> ); }
|
useReducer
Flux/Redux思想
useState的复杂版,常用于表单
-
创建初始值
-
创建操作合集reducer(state,action)
-
传给useReducer,得到读写API
-
调用写API,传一个type ({type:‘操作类型’})
代替Redux
-
将数据集中在一个store对象
-
将操作集中在reducer
-
创建一个Context
-
创建对数据的读写api
-
将读写api放入Context
-
用Context.Provider把api提供给所有组件
-
各个组件用useContext获取读写api
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
| const initial = { n: 0 };
const reducer = (state, action) => { if (action.type === "add") { return { n: state.n + action.number }; } else if (action.type === "multi") { return { n: state.n * 2 }; } else { throw new Error("unknown type"); } };
function App() { const [state, dispatch] = useReducer(reducer, initial); const { n } = state; const onClick = () => { dispatch({ type: "add", number: 1 }); }; const onClick2 = () => { dispatch({ type: "add", number: 2 }); }; return ( <div className="App"> <h1>n: {n}</h1> <button onClick={onClick}>+1</button> <button onClick={onClick2}>+2</button> </div> ); }
|
useContext
上下文,相当于在某一范围中充当「全局变量」的作用
从上至下逐级更新的过程,不是响应式过程(监听数据变化并做出反应)
-
创建一个Context
1
| const C = createContext(null)
|
-
用C.Provider圈定范围
1 2 3 4 5
| <C.Provider value={}> const [ n, setN ] = useState(0) <ComponentA /> <ComponentB /> </C.Provider>
|
-
在组件中使用
1
| const { n, setN } = useContext(C)
|
useEffect
每次render后调用的函数
在函数组件中,作为componentDidMount, componentDidUpdate, componentWillUnmount使用
多个useEffect会按写代码时的先后顺序执行
1 2 3 4 5 6 7 8 9
| useEffect(() => {})
useEffect(() => {}, [])
useEffect(() => {}, [n])
useEffect(() => { return () => {}})
|
useLayoutEffect
在浏览器改变外观前执行,区别于useEffect在浏览器改变外观之后
因此useLayoutEffect总是比useEffect先执行
useMemo
React.memo(ComponentA): ComponentA只在props更新后执行
1 2 3
| const fn = useMemo(() => { return () => {} }, [m,n])
|
缓存内容,在页面刷新时缓存上一次的值,并使新渲染的组件使用该缓存值,新fn和旧fn为相同的两个空函数。只有m或者n变化时,fn才会重新得到新值
如果不使用useMemo,两次得到的fn会因为函数地址不同而被认为是两个不同的函数,从而触发组件的渲染,增加了渲染量,降低了性能
useCallback
useMemo简化版
1
| const fn = useCallback(() => {}, [m,n])
|
useRef
让一个值在组件不断render时保持不变
1 2 3 4 5
| const count = useRef(0)
useEffect(()=>{ count.current += 1 })
|
此时count记录了组件渲染的次数
使用.current的原因是:使用引用,来确保每次的渲染得到的新count都是指向同一个对象的引用
注意:count.current的变化不会触发UI的更新,需要调用useState来手动更新
1 2 3 4 5 6 7 8 9
| const [随便取一个,set随便取一个] = useState(null)
const onClick = () => { count.current += 1 set随便取一个(Math.random()) }
|
forwardRef
React的函数组件需要使用forwardRef来获取外部传来的ref
1 2 3 4 5 6 7 8 9 10 11 12
| function App() { const buttonRef = useRef(null) return( <div> <Button2 ref={buttonRef}>button</Button2> </div> ) }
const Button2 = React.forwardRef((props,ref) => { return (<button ref={ref} {...props} />) }
|
useImperativeHandle
自定义ref