介绍

在 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 的作用
代码演示
image.png
详细代码

  1. import React, { useState } from 'react';
  2. import { Button } from 'antd';
  3. const Mock: React.FC<any> = () => {
  4. const [count, setCount ] = useState<number>(0)
  5. return (
  6. <div style={{display: 'flex', justifyContent: 'space-between', paddingRight: 200}}>
  7. <Button type='primary' onClick={() => setCount(count + 1)}>加1</Button>
  8. <div>{count}</div>
  9. </div>
  10. );
  11. };
  12. export default Mock;

useEffect

  • 副作用(Side Effect)是指 function 做了和本身运算返回值无关的事,如请求数据、修改全局变量,打印、数据获取、设置订阅以及手动更改 React 组件中的 DOM 都属于副作用操作都算是副作用
  • 整合原有的生命周期方法,通过第二个参数来接收

作用
副作用,主要是将组件中的 componentDidMount componentDidUpdate componentWillUnmount 到一个方法中,杜绝了频繁定义的繁琐

代码演示
image.png
详细代码

  1. import React, { useState, useEffect } from 'react';
  2. import { Button } from 'antd';
  3. const Mock: React.FC<any> = () => {
  4. const [count, setCount ] = useState<number>(0)
  5. return (
  6. <div>
  7. <Button type='primary' onClick={() => setCount(count + 1)}>加一</Button>
  8. <Test count={count} />
  9. </div>
  10. );
  11. };
  12. const Test: React.FC<{count: number}> = ({count}) => {
  13. const [count1, setCount1 ] = useState<number | false>(false)
  14. const [count2, setCount2 ] = useState<number | false>(false)
  15. useEffect(() => {
  16. setCount1(count)
  17. },[])
  18. useEffect(() => {
  19. setCount2(count)
  20. },[count])
  21. return <div style={{display: 'flex', justifyContent: 'space-between', marginRight: 200, marginTop: 50}}>
  22. <div>只执行一次: {count1}</div>
  23. <div>执行多次: {count2}</div>
  24. </div>
  25. }
  26. export default Mock;

useContext

  • useContext 实现跨层级传递,实现数据共享
  • 作用就是怼他包含的组件树提供全局共享的数据的一种技术
  • 需要createContext的帮助,通过 CountContext.Provider 包裹的组件,才能通过 useContext 获取对应的值

作用
上下文,对应的Context,其本意就是设置全局共享数据,使所有组件可跨层级实现共享
代码演示
image.png
详细代码

  1. import React, { useState, createContext, useContext } from 'react';
  2. import { Button } from 'antd';
  3. const CountContext = createContext(-1)
  4. const Mock: React.FC<any> = () => {
  5. const [count, setCount ] = useState<number>(0)
  6. return (
  7. <div>
  8. <Button type='primary' onClick={() => setCount(count + 1)}>加1</Button>
  9. <CountContext.Provider value={count}>
  10. <Test1 />
  11. </CountContext.Provider>
  12. </div>
  13. );
  14. };
  15. const Test1: React.FC<any> = () => {
  16. const count = useContext(CountContext)
  17. return <div style={{marginTop: 20}}>
  18. 子组件获取到的count: {count}
  19. <Test2 />
  20. </div>
  21. }
  22. const Test2: React.FC<any> = () => {
  23. const count = useContext(CountContext)
  24. return <div style={{marginTop: 20}}>
  25. 孙组件获取到的count: {count}
  26. </div>
  27. }
  28. export default Mock;

useReducer

  • 设置统一状态
  • 接收两个参数,分别为 state action,然后返回一个状态的 count(属性值) 和 dispatch(方法)

作用
作用类似于 redux, 增强函数体
代码演示
image.png
详细代码

  1. import React, { useState, useReducer } from 'react';
  2. import { Button } from 'antd';
  3. const Mock: React.FC<any> = () => {
  4. const [count, dispatch] = useReducer((state:any, action: any)=> {
  5. switch(action?.type){
  6. case 'add':
  7. return state + action?.payload;
  8. case 'sub':
  9. return state - action?.payload;
  10. default:
  11. return state;
  12. }
  13. }, 0);
  14. return (
  15. <div>
  16. <div style={{display: 'flex', justifyContent: 'flex-start'}}>
  17. <Button type='primary' onClick={() => dispatch({type: 'add', payload: 1})}>加1</Button>
  18. <Button type='primary' onClick={() => dispatch({type: 'sub', payload: 1})} style={{marginLeft: 24}}>减1</Button>
  19. </div>
  20. <div style={{marginTop: 20}}>count: {count}</div>
  21. </div>
  22. );
  23. };
  24. export default Mock;

useMemo

  • useMemo 的使用和 useEffect 的使用方式基本一致
  • 当组件进行更新时,虽然子组件不会改变状态,但还是会进行刷新,而 useMemo 只监听特定的值,也就是说,当这个值没有发生变化时,不会更新
  • 在 useMemo 函数内通过复杂计算获取当前值得时候,不需要再父组件每次更新的时候重新计算,只要在依赖项发生变化的时候计算即可

作用
当一个父组件中调用了一个子组件的时候,父组件的 state 发生变化,会导致父组件更新,而子组件虽然没有发生改变,但也会进行更新。useMemo 就是函数组件为了防止这种不必要的而采取的手段,所以一般 useMemo 是优化手段
代码演示
image.png
详细代码

  1. import React, { useState, useMemo } from 'react';
  2. import { Button } from 'antd';
  3. const Mock: React.FC<any> = () => {
  4. const [count, setCount ] = useState<number>(0)
  5. const add = useMemo(() => {
  6. return count + 1
  7. }, [count])
  8. return (
  9. <div style={{display: 'flex', justifyContent: 'space-between', paddingRight: 50}}>
  10. <Button type='primary' onClick={() => setCount(count + 1)}>加1</Button>
  11. <div>count: {count}</div>
  12. <div>次数: {add}</div>
  13. </div>
  14. );
  15. };
  16. export default Mock;

useCallback

通常在将一个组件中的函数,传递给子元素进行回调使用时,使用useCallback对函数进行处理
作用
与 useMemo 方法类似,只是 useMemo 缓存的是变量, 而 useCallBack 缓存的是函数
代码演示
image.pngimage.png
详细代码

  1. import React, { useState, useCallback } from 'react';
  2. import { Button } from 'antd';
  3. const MockMemo: React.FC<any> = () => {
  4. const [count,setCount] = useState(0)
  5. const [show,setShow] = useState(true)
  6. const add = useCallback(()=>{
  7. setCount(count + 1)
  8. },[count])
  9. return (
  10. <div>
  11. <div style={{display: 'flex', justifyContent: 'flex-start'}}>
  12. <TestButton title="普通点击" onClick={() => setCount(count + 1) }/>
  13. <TestButton title="useCallback点击" onClick={add}/>
  14. </div>
  15. <div style={{marginTop: 20}}>count: {count}</div>
  16. <Button onClick={() => {setShow(!show)}}> 切换</Button>
  17. </div>
  18. )
  19. }
  20. const TestButton = memo((props:any)=>{
  21. console.log(props.title)
  22. return <Button type='primary' onClick={props.onClick} style={props.title === 'useCallback点击' ? {
  23. marginLeft: 20
  24. } : undefined}>{props.title}</Button>
  25. })
  26. export default MockMemo;

useRef

  • useRef 类似于类组件的 this
  • 可以传入初始值(initialValue),并且这个对象只有一个 current属性
  • useRef 不会随着渲染,生命周期而改变,这点与 createRef 有着本质区别

作用
useRef 获取当前元素的所有属性,并且返回一个可变的ref对象,并且这个对象只有current属性,可设置initialValue
代码演示
image.png
image.png
详细代码

  1. import React, { useState, useRef } from 'react';
  2. const Mock: React.FC<any> = () => {
  3. const scrollRef = useRef<any>(null);
  4. const [clientHeight, setClientHeight ] = useState<number>(0)
  5. const [scrollTop, setScrollTop ] = useState<number>(0)
  6. const [scrollHeight, setScrollHeight ] = useState<number>(0)
  7. const onScroll = () => {
  8. if(scrollRef?.current){
  9. let clientHeight = scrollRef?.current.clientHeight; //可视区域高度
  10. let scrollTop = scrollRef.current.scrollTop; //滚动条滚动高度
  11. let scrollHeight = scrollRef.current.scrollHeight; //滚动内容高度
  12. setClientHeight(clientHeight)
  13. setScrollTop(scrollTop)
  14. setScrollHeight(scrollHeight)
  15. }
  16. }
  17. return (
  18. <div >
  19. <div >
  20. <p>可视区域高度:{clientHeight}</p>
  21. <p>滚动条滚动高度:{scrollTop}</p>
  22. <p>滚动内容高度:{scrollHeight}</p>
  23. </div>
  24. <div style={{height: 200, overflowY: 'auto'}} ref={scrollRef} onScroll={onScroll} >
  25. <div style={{height: 2000}}></div>
  26. </div>
  27. </div>
  28. );
  29. };
  30. export default Mock;
  1. import React, { useState, useRef, createRef } from 'react';
  2. import { Button } from 'antd';
  3. const CreateMock: React.FC<any> = () => {
  4. const [renderIndex, setRenderIndex] = React.useState(1);
  5. const refFromUseRef = React.useRef<number>();
  6. const refFromCreateRef = createRef<any>();
  7. if (!refFromUseRef.current) {
  8. refFromUseRef.current = renderIndex;
  9. }
  10. if (!refFromCreateRef.current) {
  11. // @ts-ignore
  12. refFromCreateRef.current = renderIndex;
  13. }
  14. return <>
  15. <p>Current render index: {renderIndex}</p>
  16. <p>refFrom UseRef: {refFromUseRef.current}</p>
  17. <p>refFrom CreateRef: {refFromCreateRef.current}</p>
  18. <Button type='primary' onClick={() => setRenderIndex(renderIndex + 1)} >点击</Button>
  19. </>
  20. };
  21. export default CreateMock;

useImperativeHandle

当一个页面很复杂的时候,我们会将这个页面进行模块化,这样会分成很多个模块,有的时候我们需要在最外层的组件上控制其他组件的方法,希望最外层的点击事件,同时执行子组件的点击事件,这时就需要 useImperativeHandle 的帮助

作用
可以让你在使用 ref 时自定义暴露给父组件的实例值。

代码演示
image.png
详细代码

  1. import React, { useState, useImperativeHandle, useRef } from 'react';
  2. import { Button } from 'antd';
  3. const Children: React.FC<any> = ({cRef}) => {
  4. const [count, setCount] = useState<number>(0)
  5. const add = () => {
  6. setCount((c) => c + 1)
  7. }
  8. useImperativeHandle(cRef, () => ({
  9. add
  10. }))
  11. return <div style={{marginBottom: 20}}>
  12. <p>点击次数:{count}</p>
  13. <Button type='primary' onClick={() => add()}>加1</Button>
  14. </div>
  15. }
  16. const Mock: React.FC<any> = () => {
  17. const ref = useRef<any>(null)
  18. return (
  19. <div>
  20. <Children cRef={ref} />
  21. <Button type='primary' onClick={() => {
  22. ref.current.add()
  23. }}>父节点加1</Button>
  24. </div>
  25. );
  26. };
  27. export default Mock;