介绍
在 React 中 有两种类型,一种是 class 一种是 函数式, 他们两个有何区别呢,那就是状态,class 相对于函数式的写法相对麻烦,但包含生命周期,可改变render,函数式组件结构简单,但无法动态的更改 render。
为了有效的使开发更加简洁,方便,React 在 16.8 的版本上 推出了 Hook,以此来解决 函数式组件 没有状态的问题,从而达到快速开发
另外 Ant Design pro V5 上也推崇 Hook 写法,所以我们来讲解下 Hook,让其更好的了解、使用 React ~
具体的项目展示:Domesy/Hook
useState
在 Class 中,我们定义变量在 constructor 中设置 this.state 设置变量,而在 Hook 中我们使用 useState
作用:
用来声明状态变量, 相当于 class 中的 this.state 和 this.setState 的作用
代码演示
详细代码
import React, { useState } from 'react';import { Button } from 'antd';const Mock: React.FC<any> = () => {const [count, setCount ] = useState<number>(0)return (<div style={{display: 'flex', justifyContent: 'space-between', paddingRight: 200}}><Button type='primary' onClick={() => setCount(count + 1)}>加1</Button><div>{count}</div></div>);};export default Mock;
useEffect
- 副作用(Side Effect)是指 function 做了和本身运算返回值无关的事,如请求数据、修改全局变量,打印、数据获取、设置订阅以及手动更改 React 组件中的 DOM 都属于副作用操作都算是副作用
- 整合原有的生命周期方法,通过第二个参数来接收
作用
副作用,主要是将组件中的 componentDidMount componentDidUpdate componentWillUnmount 到一个方法中,杜绝了频繁定义的繁琐
代码演示
详细代码
import React, { useState, useEffect } from 'react';import { Button } from 'antd';const Mock: React.FC<any> = () => {const [count, setCount ] = useState<number>(0)return (<div><Button type='primary' onClick={() => setCount(count + 1)}>加一</Button><Test count={count} /></div>);};const Test: React.FC<{count: number}> = ({count}) => {const [count1, setCount1 ] = useState<number | false>(false)const [count2, setCount2 ] = useState<number | false>(false)useEffect(() => {setCount1(count)},[])useEffect(() => {setCount2(count)},[count])return <div style={{display: 'flex', justifyContent: 'space-between', marginRight: 200, marginTop: 50}}><div>只执行一次: {count1}</div><div>执行多次: {count2}</div></div>}export default Mock;
useContext
- useContext 实现跨层级传递,实现数据共享
- 作用就是怼他包含的组件树提供全局共享的数据的一种技术
- 需要createContext的帮助,通过 CountContext.Provider 包裹的组件,才能通过 useContext 获取对应的值
作用
上下文,对应的Context,其本意就是设置全局共享数据,使所有组件可跨层级实现共享
代码演示
详细代码
import React, { useState, createContext, useContext } from 'react';import { Button } from 'antd';const CountContext = createContext(-1)const Mock: React.FC<any> = () => {const [count, setCount ] = useState<number>(0)return (<div><Button type='primary' onClick={() => setCount(count + 1)}>加1</Button><CountContext.Provider value={count}><Test1 /></CountContext.Provider></div>);};const Test1: React.FC<any> = () => {const count = useContext(CountContext)return <div style={{marginTop: 20}}>子组件获取到的count: {count}<Test2 /></div>}const Test2: React.FC<any> = () => {const count = useContext(CountContext)return <div style={{marginTop: 20}}>孙组件获取到的count: {count}</div>}export default Mock;
useReducer
- 设置统一状态
- 接收两个参数,分别为 state action,然后返回一个状态的 count(属性值) 和 dispatch(方法)
作用
作用类似于 redux, 增强函数体
代码演示
详细代码
import React, { useState, useReducer } from 'react';import { Button } from 'antd';const Mock: React.FC<any> = () => {const [count, dispatch] = useReducer((state:any, action: any)=> {switch(action?.type){case 'add':return state + action?.payload;case 'sub':return state - action?.payload;default:return state;}}, 0);return (<div><div style={{display: 'flex', justifyContent: 'flex-start'}}><Button type='primary' onClick={() => dispatch({type: 'add', payload: 1})}>加1</Button><Button type='primary' onClick={() => dispatch({type: 'sub', payload: 1})} style={{marginLeft: 24}}>减1</Button></div><div style={{marginTop: 20}}>count: {count}</div></div>);};export default Mock;
useMemo
- useMemo 的使用和 useEffect 的使用方式基本一致
- 当组件进行更新时,虽然子组件不会改变状态,但还是会进行刷新,而 useMemo 只监听特定的值,也就是说,当这个值没有发生变化时,不会更新
- 在 useMemo 函数内通过复杂计算获取当前值得时候,不需要再父组件每次更新的时候重新计算,只要在依赖项发生变化的时候计算即可
作用
当一个父组件中调用了一个子组件的时候,父组件的 state 发生变化,会导致父组件更新,而子组件虽然没有发生改变,但也会进行更新。useMemo 就是函数组件为了防止这种不必要的而采取的手段,所以一般 useMemo 是优化手段
代码演示
详细代码
import React, { useState, useMemo } from 'react';import { Button } from 'antd';const Mock: React.FC<any> = () => {const [count, setCount ] = useState<number>(0)const add = useMemo(() => {return count + 1}, [count])return (<div style={{display: 'flex', justifyContent: 'space-between', paddingRight: 50}}><Button type='primary' onClick={() => setCount(count + 1)}>加1</Button><div>count: {count}</div><div>次数: {add}</div></div>);};export default Mock;
useCallback
通常在将一个组件中的函数,传递给子元素进行回调使用时,使用useCallback对函数进行处理
作用
与 useMemo 方法类似,只是 useMemo 缓存的是变量, 而 useCallBack 缓存的是函数
代码演示

详细代码
import React, { useState, useCallback } from 'react';import { Button } from 'antd';const MockMemo: React.FC<any> = () => {const [count,setCount] = useState(0)const [show,setShow] = useState(true)const add = useCallback(()=>{setCount(count + 1)},[count])return (<div><div style={{display: 'flex', justifyContent: 'flex-start'}}><TestButton title="普通点击" onClick={() => setCount(count + 1) }/><TestButton title="useCallback点击" onClick={add}/></div><div style={{marginTop: 20}}>count: {count}</div><Button onClick={() => {setShow(!show)}}> 切换</Button></div>)}const TestButton = memo((props:any)=>{console.log(props.title)return <Button type='primary' onClick={props.onClick} style={props.title === 'useCallback点击' ? {marginLeft: 20} : undefined}>{props.title}</Button>})export default MockMemo;
useRef
- useRef 类似于类组件的 this
- 可以传入初始值(initialValue),并且这个对象只有一个 current属性
- useRef 不会随着渲染,生命周期而改变,这点与 createRef 有着本质区别
作用
useRef 获取当前元素的所有属性,并且返回一个可变的ref对象,并且这个对象只有current属性,可设置initialValue
代码演示

详细代码
import React, { useState, useRef } from 'react';const Mock: React.FC<any> = () => {const scrollRef = useRef<any>(null);const [clientHeight, setClientHeight ] = useState<number>(0)const [scrollTop, setScrollTop ] = useState<number>(0)const [scrollHeight, setScrollHeight ] = useState<number>(0)const onScroll = () => {if(scrollRef?.current){let clientHeight = scrollRef?.current.clientHeight; //可视区域高度let scrollTop = scrollRef.current.scrollTop; //滚动条滚动高度let scrollHeight = scrollRef.current.scrollHeight; //滚动内容高度setClientHeight(clientHeight)setScrollTop(scrollTop)setScrollHeight(scrollHeight)}}return (<div ><div ><p>可视区域高度:{clientHeight}</p><p>滚动条滚动高度:{scrollTop}</p><p>滚动内容高度:{scrollHeight}</p></div><div style={{height: 200, overflowY: 'auto'}} ref={scrollRef} onScroll={onScroll} ><div style={{height: 2000}}></div></div></div>);};export default Mock;
import React, { useState, useRef, createRef } from 'react';import { Button } from 'antd';const CreateMock: React.FC<any> = () => {const [renderIndex, setRenderIndex] = React.useState(1);const refFromUseRef = React.useRef<number>();const refFromCreateRef = createRef<any>();if (!refFromUseRef.current) {refFromUseRef.current = renderIndex;}if (!refFromCreateRef.current) {// @ts-ignorerefFromCreateRef.current = renderIndex;}return <><p>Current render index: {renderIndex}</p><p>refFrom UseRef: {refFromUseRef.current}</p><p>refFrom CreateRef: {refFromCreateRef.current}</p><Button type='primary' onClick={() => setRenderIndex(renderIndex + 1)} >点击</Button></>};export default CreateMock;
useImperativeHandle
当一个页面很复杂的时候,我们会将这个页面进行模块化,这样会分成很多个模块,有的时候我们需要在最外层的组件上控制其他组件的方法,希望最外层的点击事件,同时执行子组件的点击事件,这时就需要 useImperativeHandle 的帮助
作用
可以让你在使用 ref 时自定义暴露给父组件的实例值。
代码演示
详细代码
import React, { useState, useImperativeHandle, useRef } from 'react';import { Button } from 'antd';const Children: React.FC<any> = ({cRef}) => {const [count, setCount] = useState<number>(0)const add = () => {setCount((c) => c + 1)}useImperativeHandle(cRef, () => ({add}))return <div style={{marginBottom: 20}}><p>点击次数:{count}</p><Button type='primary' onClick={() => add()}>加1</Button></div>}const Mock: React.FC<any> = () => {const ref = useRef<any>(null)return (<div><Children cRef={ref} /><Button type='primary' onClick={() => {ref.current.add()}}>父节点加1</Button></div>);};export default Mock;
