【Примечания】Съедобный реактивный хук, предотвращающий дрожание и дросселирование | Спасибо, что забрали его 🙈🙈🙈

JavaScript
【Примечания】Съедобный реактивный хук, предотвращающий дрожание и дросселирование | Спасибо, что забрали его 🙈🙈🙈

новое хранилище знанийПередняя часть от входа до входаПросите внимания, помечайте звездочками и предложениями и время от времени обновляйте~

Почему бы вам не посмотреть выпуски:Большое путешествие по построению фундамента с нуля (подробное объяснение, постоянное обновление~)
Каталог длиной 10 000 символов, если вы считаете, что это хорошо, просто поставьте лайк~

Если слева меньше 30 лайков, пожалуйста, нажмите, спасибо~

Дебаунс и дроссель — это инструментальные функции, которые часто используются во внешнем интерфейсе.

При выполнении таких операций, как изменение размера окна, прокрутка, проверка содержимого поля ввода и т. д., если частота вызовов обработчика событий неограничена, это увеличит нагрузку на браузер, что приведет к очень плохому взаимодействию с пользователем. На этом этапе мы можем использовать debounce (анти-дрожание) и Throttle (дросселирование), чтобы уменьшить частоту вызовов, не влияя на фактический эффект.

Обычно мы привыкли использоватьlodashПредоставленные функции инструмента просты и удобны, но всегда ли фермеры должны брать интервью?

Функция защиты от тряски

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

Давайте реализуем простой дебаунс вместе~

function debounce(fn, ms) {
  let timer;
  return function(...args) {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn(...args)
      timer = null;
    }, ms);
  }
}

Важно помнить, что функцию debounce следует вызывать там, где она выполняется только один раз.

регулирование функции

Когда событие запускается постоянно, гарантируется, что обработчик события вызывается только один раз в течение определенного периода времени.

function throttle(fn, ms) {            
    let timer;        
    return function(...args) {                
        if (timer) return;
        canRun = false;
        timer = setTimeout(() => { 
            fn(...args);
            timer = null;
        }, ms);          
    }        
}        

Важно помнить, что функцию debounce следует вызывать там, где она выполняется только один раз.

Принцип работы функции троттлинга и функции антитряски на самом деле очень прост.

  • Anti-shake: поддерживать таймер, который предусматривает, что функция срабатывает после времени задержки, но если она снова срабатывает в течение времени задержки, предыдущий таймер будет отменен и сброшен. Таким образом, может быть запущено только последнее действие.
  • Регулирование: поддерживать таймер, который предусматривает, что функция запускается после времени задержки, но если она снова запускается в течение времени задержки, он определяет, существует ли отложенная вызывающая функция, которая не была выполнена, и если да, то она вернется, а если нет, будет настроено активировать функцию по истечении времени задержки.

Следующее обновление, как добиться функции защиты от сотрясений и дросселирования в реагирующем хуке?

Крюк против тряски

По простой аналогии наверное есть такая функция

function useDebounce(fn, time) {
  return debounce(fn, time);
}

Отлично, попробуйте написать функцию

export default function() {
  const [counter, setCounter] = useState(0);

  const handleClick = useDebounce(function() {
    setCounter(counter + 1)
  }, 1000)

  return <div style={{ padding: 30 }}>
    <Button
      onClick={handleClick}
    >click</Button>
    <div>{counter}</div>
  </div>
}

Нажмите два раза, чтобы попробовать, а, это очень полезно, да, просто чувствуется, что что-то не так. . .

изменить образец

export default function() {
  const [counter1, setCounter1] = useState(0);
  const [counter2, setCounter2] = useState(0);

  const handleClick = useDebounce(function() {
    setCounter1(counter1 + 1)
  }, 500)
 // 补充一个函数,加载后会自动更新counter2的数值 
  useEffect(function() {
    const t = setInterval(() => {
      setCounter2(x => x + 1)
    }, 500);
    return () => clearInterval(t)
  }, [])


  return <div style={{ padding: 30 }}>
    <Button
      onClick={function() {
        handleClick()
      }}
    >click</Button>
    <div>{counter1}</div>
    <div>{counter2}</div>
  </div>
}

Разве это не просто добавление автоматического обновления, дважды щелкните... о. . . не работает антивибрация

Помните приведенное выше предложение, функция debounce должна вызываться в месте, где она выполняется только один раз. В компоненте класса поместитеconstructorЕго можно использовать при генерации переменной функции, потому что компонент класса будет инициализирован только один раз, а функции, связанные в последующих компонентах, никогда не изменятся, поэтому будет работать состояние, сохраненное по принципу замыкания.

В функциональных компонентах внутренняя функция регенерируется и привязывается к компоненту при каждом его отображении. Когда компонент имеет только одно состояние, это повлияетrenderкогда мы

  1. сумасшедшая кнопка,
  2. вызовет только событие клика, а не повторный рендеринг,
  3. Функция события, привязанная к текущему компоненту, не изменилась, а функция защиты от сотрясений осталась прежней, поэтому защита от сотрясений работает.

но когда есть другиеstateПосле рендеринга

  1. Сумасшедшая кнопка
  2. Запустить событие без повторного рендеринга
  3. count2изменить, перерисовать
  4. handleClick регенерируется и привязывается к компоненту,
  5. Исходная функция не работает, анти-тряска не работает, и исходная функция выполняется после определенной задержки.
  6. counter1изменять

Сравнение процесса такое, теперь вы понимаете, почему обычную функцию защиты от тряски нельзя использовать вreack hookТы в деле?

Итак, как добитьсяreact hookА как насчет защиты от тряски? Основная идея состоит в том, чтобы гарантировать, что функция, связанная с компонентом, будет одной и той же функцией защиты от сотрясений при каждом рендеринге.

Давайте пошагово проведем аналогию.Во-первых, раз уж мы хотим обеспечить ту же антивибрационную функцию, то попробуемuseCallbackилиuseMemoНу, этот хук может гарантировать, что одно и то же значение будет возвращено, когда зависимость останется неизменной.

Давайте добавим слой упаковки, полагаясь на передачу пустого массива, чтобы гарантироватьuseCallbackвсегда возвращать одну и ту же функцию

function useDebounce(fn, delay) {
  return useCallback(debounce(fn, delay), [])
}

export default function() {
  const [counter, setCounter] = useState(0);

  const handleClick = useDebounce(function() {
    setCounter(counter + 1)
  }, 1000)

  return <div style={{ padding: 30 }}>
    <Button
      onClick={handleClick}
    >click</Button>
    <div>{counter}</div>
  </div>
}

Используйте один счетчик для отладки на этот раз, угадайте результат?

После изменения счетчика с 0 на 1 он не изменится. Зачем? если ты понимаешьПринцип закрытия, тогда вы сможете понять концепцию моментальных снимков.

благодаря нашемуuseCallbackЗависимость представляет собой пустой массив, поэтому после завершения инициализации компонентаhandleClickФункция всегда является снимком функции при инициализации, то есть она не будет обновляться при повторном рендеринге последующих компонентов.handleClick,в то же время,handleClickдержалcounterЭто также моментальный снимок, когда эта функция была создана, т. е. она всегда0, Таким образом, даже если функция защиты от сотрясений останется неизменной, она не сможет заставить программу работать нормально.

Конечно можно пройти setCounter(x => x + 1)чтобы получить правильныйcounterзначение, но другие сценарии не применяются.

Что еще может гарантировать уникальность данных?useRef~ Проблема с описанным выше методом заключается в том, что либо функция защиты от сотрясений не может быть гарантирована уникальной, что приводит к потере действия таймера, либо нельзя гарантировать, что вызывающая функция является самой последней, что приводит к тому, что вызывающая функция теряет свой эффект. Нейтрализуйте два метода, и результат выйдет.

function useDebounce(fn, delay, dep = []) {
  const { current } = useRef({ fn, timer: null });
  useEffect(function () {
    current.fn = fn;
  }, [fn]);

  return useCallback(function f(...args) {
    if (current.timer) {
      clearTimeout(current.timer);
    }
    current.timer = setTimeout(() => {
      current.fn(...args);
    }, delay);
  }, dep)
}

Готово ~ Теперь у вас есть функция защиты от тряски, которую вы можете убрать напрямую.

Дросселирование крюка

function useThrottle(fn, delay, dep = []) {
  const { current } = useRef({ fn, timer: null });
  useEffect(function () {
    current.fn = fn;
  }, [fn]);

  return useCallback(function f(...args) {
    if (!current.timer) {
      current.timer = setTimeout(() => {
        delete current.timer;
      }, delay);
      current.fn(...args);
    }
  }, dep);
}

Эпилог

После контакта с реактивными хуками большинство компонентов являются непосредственно функциональными компонентами в виде хуков, однако стоит отметить, что мы используем хуки для более качественной и удобной реализации компонентов, таких как

  • Не нужно думать, как обновить состояние при изменении реквизита,
  • Нет необходимости рассматривать использованиеgetDerivedStateFromPropsКак инициировать некоторые действия после обновления данных,
  • Или не нужно специально рассматривать как пройтиshouldComponentUpdateЧтобы написать сложную логику оценки, чтобы уменьшить ненужный рендеринг.

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

Если вы получите новые знания, пожалуйста, поставьте лайк и расскажите мне~

Эта статья включена в:Большое путешествие по построению фундамента с нуля (подробное объяснение, постоянное обновление~)

Рекомендуемое чтение:

Справочная документация

  1. Как хуки React защищают от тряски?
  2. js анти-дрожание и дросселирование