Получите общие пользовательские хуки React в одной статье

React.js внешний фреймворк
Получите общие пользовательские хуки React в одной статье

предисловие

через предыдущий пост«Одна статья обобщает распространенные сценарии React Hooks», мы приведем примеры в соответствии со сценариями использования, чтобы помочь вам понять и умело использовать большинство функций React Hooks. В этой статье хуки еще больше углубляются. Давайте настроим некоторые хуки для решения сценариев спроса, которые мы часто используем в наших обычных проектах, чтобы добиться высокого уровня повторного использования кода и низкой связанности, чтобы углубить понимание и применение хуков.

Я много работал над организацией в течение долгого времени, и я надеюсь вручную поставить лайк и поощрить ~ Адрес блога на github:GitHub.com/ревматизм123/…, в котором собраны все блоги автора, приглашаем подписаться и отметиться ~

1. Реализовать кастомное useMount

Во-первых, мы настраиваем хук useMount, функция которого заключается в выполнении связанных функций после рендеринга Dom, что похоже на функцию хука жизненного цикла componentDidMount в методе записи компонента класса. Наша реализация основана на следующем принципе: если вы хотите выполнить эффект, который запускается только один раз (только при монтировании и размонтировании компонента), вы можете передать пустой массив ([]) в качестве второго параметра. Это сообщает React, что ваш эффект не зависит ни от каких значений в свойствах или состоянии, поэтому его никогда не нужно повторять. Если функция реализована в функциональном компоненте, код выглядит следующим образом

useEffect(() => {
  console.log('mount');
}, []);

Теперь мы извлекаем эту функцию и инкапсулируем ее как хук useMount, который может быть реализован следующим образом, где хук поддерживает передачу функции выполнения обратного вызова fn в качестве параметра.

import { useEffect } from 'react';

const useMount = (fn: () => void) => {
  useEffect(() => {
    fn();
  }, []);
};

export default useMount;

Теперь мы можем использовать этот хук useMount в связанных бизнес-сценариях, как показано ниже, запускать забаву только один раз при первом отображении MyPage, даже если мы нажмем кнопку несколько раз, счетчик будет продолжать увеличиваться, и страница будет продолжать обновляться. , Казнить весело.

import React, { useCallback, useState } from 'react';
import useMount from './useMount';

const MyPage = () => {
  const [count, setCount] = useState(0);
  const fun = useCallback(() => {
    console.log('mount');
  }, []);

  useMount(fun);

  return (
    <div >
      <button type="button" onClick={() => { setCount(count + 1); }}>
        增加 {count}
      </button>
    </div>
  );
};

export default MyPage;

2. Реализовать кастомное useUnmount

В этом разделе мы настраиваем хук useUnmount, функция которого заключается в выполнении связанных функций до размонтирования Dom, что аналогично функции хука жизненного цикла componentWillUnmount в методе записи компонента класса. Наша реализация основана на следующем принципе: если эффект возвращает функцию, React вызовет ее при выполнении операции очистки. Если функция реализована в функциональном компоненте, код выглядит следующим образом

useEffect(() => () => {
  console.log('unmount');
});

Теперь мы извлекаем эту функцию и инкапсулируем ее как хук useUnmount, который можно реализовать следующим образом, где хук поддерживает передачу функции выполнения обратного вызова fn в качестве параметра.

import { useEffect } from 'react';

const useUnmount = (fn: () => void) => {
  useEffect(() => () => {
    fn();
  }, []);
};

export default useUnmount;

Теперь мы можем использовать этот хук useUnmount в связанных бизнес-сценариях, как показано ниже, забава будет выполняться только один раз, когда MyComponet размонтирован.

import React, { useCallback, useState } from 'react';
import useUnmount from './useUnmount';

const MyComponent = () => {
  const fun = useCallback(() => {
    console.log('unmount');
  }, []);

  useUnmount(fun);

  return <div>Hello World</div>;
};


const MyPage = () => {
  const [state, setState] = useState(true);

  return (
    <div >
      {state && <MyComponent />}
      <button type="button" onClick={() => { setState(!state); }}>
        切换
      </button>
    </div>
  );
};

export default MyPage;

3. Реализовать настраиваемое useUpdate

Все мы знаем, что если мы хотим, чтобы функциональный компонент повторно отрисовывался, мы должны обновить состояние, но иногда состояние, которое нужно бизнесу, не нужно обновлять.Мы не можем заставить состояние выполнять бессмысленное обновление только для того, чтобы повторный рендеринг компонента. Итак, на данный момент мы можем настроить обновленный хук, чтобы элегантно реализовать принудительное обновление компонентов, аналогично функции forceUpdate компонентов класса. Код реализации выглядит следующим образом

import { useCallback, useState } from 'react';

const useUpdate = () => {
  const [, setState] = useState({});

  return useCallback(() => setState({}), []);
};

export default useUpdate;

Пример useUpdate показан ниже.При нажатии кнопки и вызове обновления значение Time будет изменено, указывая на то, что компонент был принудительно обновлен.

import React from 'react';
import useUpdate from './useUpdate';

const MyPage = () => {
  const update = useUpdate();

  return (
    <div >
      <button type="button" onClick={update}>
      Time: {Date.now()}
      </button>
    </div>
  );
};

export default MyPage;

4. Реализуйте настраиваемое использование. Предыдущая

Обычно при реализации требований часто необходимо сохранить значение состояния последнего рендеринга, поэтому этот хук используется для сохранения последнего состояния рендеринга. Логика реализации показана ниже, а useRef.current в основном используется для хранения переменных.

import { useRef } from 'react';

function usePrevious<T> (state: T): T|undefined {
  const prevRef = useRef<T>();
  const curRef = useRef<T>();

  prevRef.current = curRef.current;
  curRef.current = state;

  return prevRef.current;
}

export default usePrevious;

Пример usePrevious показан ниже, когда нажимается кнопка для увеличения счетчика, предыдущий сохранит предыдущее значение счетчика.

import React, { useState } from 'react';
import usePrevious from './usePrevious';

const MyPage = () => {
  const [count, setCount] = useState(0);
  const previous = usePrevious(count);

  return (
    <div >
      <div>新值:{count}</div>
      <div>旧值:{previous}</div>
      <button type="button" onClick={() => { setCount(count + 1); }}>
        增加
      </button>
    </div>
  );
};

export default MyPage;

5. Реализуйте пользовательский useTimeout

В хуке после того, как мы используем setTimeout, нам нужно вручную очистить Timeout, чтобы убрать таймер при выгрузке dom, иначе это может привести к утечке памяти. Допустим, мы используем его много раз в проекте, затем нам нужно много раз писать и удалять код, а иногда он может быть забыт из-за небрежности. Итак, почему его нельзя инкапсулировать в виде хука и вызывать при необходимости.

import { useEffect } from 'react';

function useTimeout (fn: () => void, delay: number) {
  useEffect(() => {
    const timer = setTimeout(() => {
      fn();
    }, delay);
    return () => {
      clearTimeout(timer); // 移除定时器
    };
  }, [delay]);
}

export default useTimeout;

Как показано ниже, нам нужно только сообщить useTimeout, сколько миллисекунд вызывать какой метод, не нужно думать об удалении таймера.

import React, { useState } from 'react';
import useTimeout from './useTimeout';

const MyPage = () => {
  const [count, setCount] = useState(0);

  useTimeout(() => {
    setCount(count => count + 1);
  }, 3000);

  return (
    <div >
      <button type="button">
        增加 {count}
      </button>
    </div>
  );
};

export default MyPage;

6. Реализуйте пользовательский useInterval

useInterval инкапсулирует функцию setInterval.Причина и использование такие же, как и у useTimeout, которые здесь повторяться не будут.

import { useEffect } from 'react';

function useInterval (fn: () => void, delay: number) {
  useEffect(() => {
    const timer = setInterval(() => {
      fn();
    }, delay);
    return () => {
      clearInterval(timer); // 移除定时器
    };
  }, [delay]);
}

export default useInterval;

7. Реализуйте пользовательский useDebounce

Защита от сотрясения очень распространена в нашей повседневной разработке, например: нажатие кнопки, редактирование и сохранение текста и т. Д. Чтобы пользователи не работали слишком часто, требуется обработка против сотрясения. **Определение защиты от сотрясения: когда задачи запускаются часто, код будет выполняться только один раз, если интервал между запусками задачи превышает указанный интервал. **Это похоже на сцену из жизни, например, в автобусе. В течение определенного периода времени, если пассажиры проводят картой одну за другой, чтобы сесть в автобус, водитель не будет вести машину. Когда пассажиры не проводят карту их карты, водитель будет водить. Базовая реализация функции защиты от сотрясений и соответствующие примечания показаны ниже.

function debounce(fn,wait){
    let timeout1;
    return function(){
        clearTimeout(timeout1);  // 重新清零
        let context = this;  // 保存上下文
        let args = arguments; // 获取传入的参数
        timeout1 = setTimeout(()=> {
            fn.apply(context, args);
        },wait)
    }
}

Мы пишем вышеприведенную реализацию в собственном виде хуков. Соответствующий код хука useDebounce выглядит следующим образом. Два входящих параметра: fn (выполняемый метод обратного вызова) и задержка (время защиты от сотрясения), а затем хук возвращает метод выполнения

import { useCallback, useRef } from 'react';

const useDebounce = (fn: Function, delay = 100) => {
  const time1 = useRef<any>();

  return useCallback((...args) => {
    if (time1.current) {
      clearTimeout(time1.current);
    }
    time1.current = setTimeout(() => {
      fn(...args);
    }, delay);
  }, [delay]);
};

export default useDebounce;

Теперь мы можем использовать этот хук useDebounce в связанных бизнес-сценариях. Как показано ниже, мы продолжаем нажимать кнопку, и счетчик не будет увеличиваться. Только когда интервал кликов превысит 3000 мс, счетчик будет увеличиваться.

import React, { useCallback, useState } from 'react';
import useDebounce from './useDebounce';

const MyPage = () => {
  const [count, setCount] = useState(0);
  const fun = useCallback(() => {
    setCount(count => count + 1);
  }, []);

  const run = useDebounce(fun, 3000);

  return (
    <div >
      <button type="button" onClick={() => { run(); }}>
        增加 {count}
      </button>
    </div>
  );
};

export default MyPage;

8. Реализуйте пользовательский useThrottle

Дросселирование очень распространено в нашей повседневной разработке, например, мониторинг полосы прокрутки, функция эффекта увеличительного стекла и т. Д. Нам не нужно запускать его каждый раз, когда прокручивается мышь, что может снизить частоту вычислений без траты ресурсов. Определение дросселирования:Регулирование функции означает, что метод js запускается только один раз в определенный период времени.. Аналогия со сценами из жизни, например, когда люди моргают глазами, моргает один раз в течение определенного периода времени. Ниже показана базовая реализация и соответствующие комментарии функции дросселирования, которая очень похожа на функцию защиты от сотрясений.

function throttle(fn, wait){
  let timeout;
  return function(){
      if(timeout) return; // 如果已经触发,则不再触发
      let args = arguments;
      let context = this;
      timeout = setTimeout(()=>{
        fn.apply(context,args); // 执行
        timeout = null; // 执行后,将标志设置为未触发
      },wait)
  }
}

Мы пишем вышеприведенную реализацию в собственном виде хуков.Соответствующий код хука useThrottle выглядит следующим образом.Передаются два параметра: fn (выполняемый метод обратного вызова) и задержка (время дросселя), а затем хук возвращает метод исполнения

import { useCallback, useRef } from 'react';

const useThrottle = (fn: Function, delay = 100) => {
  const time1 = useRef<any>();

  return useCallback((...args) => {
    if (time1.current) {
      return;
    }
    time1.current = setTimeout(() => {
      fn(...args);
      time1.current = null;
    }, delay);
  }, [delay]);
};

export default useThrottle;

Теперь мы можем использовать этот хук useThrottle в связанных бизнес-сценариях.Как показано ниже, мы продолжаем нажимать кнопку, и счетчик будет увеличиваться только один раз каждые 3000 мс, а не каждый клик.

import React, { useCallback, useState } from 'react';
import useThrottle from './useThrottle';

const MyPage = () => {
  const [count, setCount] = useState(0);
  const fun = useCallback(() => {
    setCount(count => count + 1);
  }, []);

  const run = useThrottle(fun, 3000);

  return (
    <div >
      <button type="button" onClick={() => { run(); }}>
        增加 {count}
      </button>
    </div>
  );
};

export default MyPage;

Суммировать

Эта статья является второй в трилогии о реагирующих хуках. Как и ожидалось, в будущем мы напишем третью в трилогии о реагирующих хуках, так что следите за обновлениями.

Я много работал над организацией в течение долгого времени, и я надеюсь вручную поставить лайк и поощрить ~ Адрес блога на github:GitHub.com/ревматизм123/…, в котором собраны все блоги автора, приглашаем подписаться и отметиться ~