hooks中一些常见用法

1、forceUpdate

在Class组件中使用this.forceUpdate()可以直接强制渲染,已最新的数据执行一次render,但是hooks是函数组件,没有当前组件的this指针,所以实现方式也有所区别

import React, { useReducer } from 'react'
export default function () {
    const [, forceUpdate] = useReducer(v => v + 1, 0)
}

使用useReducer,调用一次来触发数据更新

2、组件首次不执行

如果组件订阅了props中的某个值,然后来执行一些操作,但是我们首次不需要执行,这种情况的话使用useRef

import React, { useRef } from 'react'
export default function (props) {
    const firstUpdate = useRef(true)
    
    useEffect(() => {
        if (firstUpdate.current) {
            firstUpdate.current = false;
            return;
        }
        // 执行内容
    }, [props])
}

3、对比上次的props,如果更改才执行

在类组件componentWillReceiveProps的钩子中,能拿到上次的props和最新的props,而hooks中没有这个钩子,那我们需要对比两次的props怎么办呢?还是用useRef

import React, { useRef } from 'react'
import { isEqual } from 'lodash';

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

export default function (props) {
    const preProps = usePrevious(props);
    
    useEffect(() => {
        if (isEqual(preProps, props)) {
            return;
        }
        // 执行内容
    }, [props])
}

4、复用的组件,申明的变量需要放在函数内

  • 放在函数外,如下:
import React, { useReducer } from 'react'

let catchRecord = {}

export default function () {
    
}

如果要将这个变量缓存其他函数中的某个值,那么这个组件复用成多个组件实例之后,就会冲突,某个组件的缓存值会把其他组件的缓存值覆盖,一句话,就是他们都用的一个变量了。

  • 正确的放在函数类
import React from 'react'

export default function () {
    let catchRecord = {}
    
}

5、更新值的异步问题

class组件更新值,可以使用函数作为this.setState的参数来实现同步,也能在第二个参数作为回调函数中来执行。hooks中更新值后,立刻要以新的值来执行的话,可以采用以下几种:

  • 1、useEffect
import React, { useState } from 'react'

export default function () {
    const [name, setName] = useState('mySkey')
    
    useEffect(() => {
        // 执行内容
    }, [name])
}
  • 2、在执行函数以参数传入新的值
import React, { useState } from 'react'

export default function () {
    const [name, setName] = useState('mySkey')
    
    function changeName(newName) {
        setName(newName)
        saveName(newName)
    }
    
    function saveName(name) {
    
    }
}

6、订阅了很多数据都要来执行一种操作

比如获取列表的接口请求,我们在useEffect中订阅了很多种数据更新都要请求,那么会导致一次发多个api请求,这时候需要把请求防抖处理

import React from 'react'

export default function () {
    let timer;
    
    function query() {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
            
        }, 500)
    }
}

7、当一个state依赖另一个state时

比如表格的columns是动态的,并且里面有的数据依赖另一个state时,那么在另一个state更新时,必须重新setColumns,否则将无法生效,如下

import React, { useState, useEffect } from 'react'
import { Table } from 'antd'

function createColumns(types) {
  return [
    {
      title: '标题',
      dataIndex: 'title'
    },
    {
      title: '分类',
      dataIndex: 'type',
      render: (text) => types[text]
    },
  ]
}

export default function () {
    const [typesObj, setTypesObj] = useState({}])
    const [columns, setColumns] = useState(createColumns(typesObj))
    const [dataSource, setDataSource] = useState([])
    
    useEffect(() => {
        // 模拟延迟获取分类信息
        setTimeout(() => {
            setTypesObj({ joker: '笑话' })
        }, 1000)
    }, [])
    
    return <Table columns={columns} dataSource={dataSource} />
}

上面代码,初始状态typesObj是个空对象,那么就在createColumns传入的是个{},如果不在后续更新了typesObj之后同步更新columns的话,就无法正常显示,所以,正确做法如下:

import React, { useState, useEffect } from 'react'
import { Table } from 'antd'

function createColumns(types) {
  return [
    {
      title: '标题',
      dataIndex: 'title'
    },
    {
      title: '分类',
      dataIndex: 'type',
      render: (text) => types[text]
    },
  ]
}

export default function () {
    const [typesObj, setTypesObj] = useState({}])
    const [columns, setColumns] = useState(createColumns(typesObj))
    const [dataSource, setDataSource] = useState([])
    
    useEffect(() => {
        // 模拟延迟获取分类信息
        setTimeout(() => {
            setTypesObj({ joker: '笑话' })
        }, 1000)
    }, [])
    
    useEffect(() => {
        // 同步更新columns
        setColumns(createColumns(typesObj))
    }, [typesObj])
    
    return <Table columns={columns} dataSource={dataSource} />
}
  • 方法二:

columns使用useMemo,不使用useState来创建

import React, { useState, useEffect, useMemo } from 'react'
import { Table } from 'antd'

function createColumns(types) {
  return [
    {
      title: '标题',
      dataIndex: 'title'
    },
    {
      title: '分类',
      dataIndex: 'type',
      render: (text) => types[text]
    },
  ]
}

export default function () {
    const [typesObj, setTypesObj] = useState({}])
    const [dataSource, setDataSource] = useState([])
    const columns = useMemo(() => createColumns(typesObj), [typesObj])
    
    useEffect(() => {
        // 模拟延迟获取分类信息
        setTimeout(() => {
            setTypesObj({ joker: '笑话' })
        }, 1000)
    }, [])
    
    return <Table columns={columns} dataSource={dataSource} />
}

8、使用useCallback和useMemo提升组件性能

  • 1、将与props和state无关的函数,放在外部申明
import React, { useState } from 'react'

function formatCounter(counterVal) {
  return `The counter value is ${counterVal}`;
}

function App() {
 const [counter, setCounter] = useState(0);
 
 return (
   <div className="App">
     <div>{formatCounter(counter)}</div>
     <button onClick={()=>onClick(setCounter)}>
       Increment
     </button>
   </div>
 );
}
  • 2、useCallback

有些时候我们必须要在hooks组件内定义函数或者方法,那么推荐用useCallback缓存这个方法,当useCallback的依赖项不发生变化的时候,该函数在每次渲染的过程中不需要重新声明

useCallback接受两个参数,第一个参数是要缓存的函数,第二个参数是一个数组,表示依赖项,当依赖项改变的时候会去重新声明一个新的函数,否则就返回这个被缓存的函数.

import React, { useState, useCallback } from 'react'

function App(props) {
 const [counter, setCounter] = useState(0);
 const onClick = useCallback(()=>{
   setCounter(props.count)
 },[props.count]);
 return (
   <div className="App">
     <div>{counter}</div>
     <button onClick={onClick}>
       Increment
     </button>
   </div>
 );
}
  • 3、useMemo

    useMemo与useCallback大同小异,区别就是useMemo缓存的不是函数,缓存的是对象(可以是jsx虚拟dom对象),同样的当依赖项不变的时候就返回这个被缓存的对象,否则就重新生成一个新的对象。

import React, { useMemo } from 'react'

function App(props) {
 const [counter, setCounter] = useState(0);
 const countLabel = useMemo(()=>{
   return `${counter}-label`
 },[counter]);
 
 return (
   <div className="App">
     <div>{counter}</div>
     <div>{countLabel}</div>
   </div>
 );
}

9、useContext

和class组件的Context一致,用于多层组件传值

function useCounter() {
  let [count, setCount] = useState(0)
  let decrement = () => setCount(count - 1)
  let increment = () => setCount(count + 1)
  return { count, decrement, increment }
}

let Counter = createContext(null)

function CounterDisplay() {
  let counter = useContext(Counter)
  return (
    <div>
      <button onClick={counter.decrement}>-</button>
      <p>You clicked {counter.count} times</p>
      <button onClick={counter.increment}>+</button>
    </div>
  )
}

function App() {
  let counter = useCounter()
  return (
    <Counter.Provider value={counter}>
      <CounterDisplay />
      <CounterDisplay />
    </Counter.Provider>
  )
}

10、useImperativeHandle

useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle 应当与 forwardRef 一起使用。
我在项目中只用到了一次,主要是给父组件暴露自定义的方法,而不是一股脑的直接返回ref,房子父组件操作ref导致问题

const Video = ({ ... }: PropTypes, ref: any) => {
  const videoRef: any = useRef(null);
  const playVideo = () => {...};
  const handlePlayBtnClick = () => {...};

  useImperativeHandle(ref, () => ({
    play: playVideo,
    pause: () => {
      if (videoRef.current) {
        videoRef.current.pause();
        setVideoVisible(false);
      }
    },
  }));  

  return (
    <div className={styles.container}>
      <video ref={videoRef}>
        <source src={videoUrl} type="video/mp4" />
      </video>
    </div>
  );
};

export default React.memo(React.forwardRef(Video));

11、定时器hook

每隔一段时间执行,组件卸载时清除

export const useInterval = (callback: () => void, delay: number) => {
  const saveCallback = useRef(callback);
  useEffect(() => {
    saveCallback.current = callback;
  }, [callback]);
  useEffect(() => {
    if (delay !== null) {
      const timer = setInterval(() => saveCallback.current(), delay);
      return () => {
        if (timer) {
          clearInterval(timer); // 取消定时器;
        }
      };
    }
    return () => { };
  }, [delay]);
};

12、更新数据后回调拿到最新值

由于setState是异步的,我们需要在设置成功后拿到最新值

export function useSyncState(state) {
  const cbRef: { current: any } = useRef();
  const [data, setData] = useState(state);

  useEffect(() => {
    if (cbRef.current) cbRef.current(data);
  }, [data]);

  return [
    data,
    (val: any, callback: any) => {
      cbRef.current = callback;
      setData(val);
    },
  ];
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值