实现usePrevious

310 阅读1分钟

自定义hooks

作为react的使用者,自定义hooks是常见的业务需求,小弟技术水平有限,只能先借花献佛聊下公司里大佬们封装的自定义hooks了

import { useRef } from 'react';

export type ShouldUpdateFunc<T> = (prev: T | undefined, next: T) => boolean;

const defaultShouldUpdate = <T>(a?: T, b?: T) => a !== b;

export function usePrevious<T>(
  state: T,
  shouldUpdate: ShouldUpdateFunc<T> = defaultShouldUpdate,
): T | undefined {
  const prevRef = useRef<T>();
  const curRef = useRef<T>();

  if (shouldUpdate(curRef.current, state)) {
    prevRef.current = curRef.current;
    curRef.current = state;
  }

  return prevRef.current;
}

保存上一次状态的 Hook。

用法如下

记录上一次的count值

import { usePrevious } from '@pansy/react-hooks';
import { Button } from 'antd';
import React, { useState } from 'react';


export default () => {
  const [count, setCount] = useState(0);
  const previous = usePrevious(count);
  return (
    <>
      <div>counter current value: {count}</div>
      <div style={{ marginBottom: 8 }}>counter previous value: {previous}</div>
      <Button onClick={() => setCount((c) => c + 1)}>
        increase
      </Button>
      <Button style={{ marginLeft: 8 }} onClick={() => setCount((c) => c - 1)}>
        decrease
      </Button>
    </>
  );
};

只有 shouldUpdate function 返回 true 时,才会记录值的变化。

import React, { useState } from 'react';
import { Button, Input } from 'antd';
import { usePrevious } from '@pansy/react-hooks';

interface Person {
  name: string;
  job: string;
}

const nameCompareFunction = (prev: Person | undefined, next: Person) => {
  if (!prev) {
    return true;
  }
  if (prev.name !== next.name) {
    return true;
  }
  return false;
};

const jobCompareFunction = (prev: Person | undefined, next: Person) => {
  if (!prev) {
    return true;
  }
  if (prev.job !== next.job) {
    return true;
  }
  return false;
};

export default () => {
  const [state, setState] = useState({ name: 'Jack', job: 'student' });
  const [nameInput, setNameInput] = useState('');
  const [jobInput, setJobInput] = useState('');
  const previousName = usePrevious(state, nameCompareFunction);
  const previousJob = usePrevious(state, jobCompareFunction);

  return (
    <>
      <div style={{ margin: '8px 0', border: '1px solid #e8e8e8', padding: 8 }}>
        <div>current name: {state.name}</div>
        <div>current job: {state.job}</div>
      </div>
      <div>previous name: {(previousName || {}).name}</div>
      <div style={{ marginBottom: 8 }}>previous job: {(previousJob || {}).job}</div>
      <div style={{ marginTop: 8 }}>
        <Input
          style={{ width: 220 }}
          value={nameInput}
          onChange={(e) => setNameInput(e.target.value)}
          placeholder="new name"
        />
        <Button
          onClick={() => {
            setState((s) => ({ ...s, name: nameInput }));
          }}
          style={{ marginLeft: 8 }}
        >
          update
        </Button>
      </div>
      <div style={{ marginTop: 8 }}>
        <Input
          style={{ width: 220 }}
          value={jobInput}
          onChange={(e) => setJobInput(e.target.value)}
          placeholder="new job"
        />
        <Button
          onClick={() => {
            setState((s) => ({ ...s, job: jobInput }));
          }}
          style={{ marginLeft: 8 }}
        >
          update
        </Button>
      </div>
    </>
  );
};

个人的分享记录,不喜勿喷。