React useEffect, ссылающийся на неверные значения - PullRequest
2 голосов
/ 06 марта 2019

Я работаю над созданием компонента для внешней библиотеки, которую я хочу использовать в своем проекте ( noUiSlider ).

В настоящее время это мой Slider компонент:

function Slider(props) {
  const { defaultMin, defaultMax, step } = props;
  const sliderElement = useRef();

  useEffect(() => {
    if (!sliderElement.current) {
      return;
    }

    noUiSlider.create(sliderElement.current, {
      connect: true,
      start: [defaultMin, defaultMax],
      step,
      range: {
        min: [defaultMin],
        max: [defaultMax],
      },
    });

    if (props.onUpdate) {
      sliderElement.current.noUiSlider.on('update', props.onUpdate);
    }

    if (props.onEnd) {
      sliderElement.current.noUiSlider.on('end', props.onEnd);
    }
  }, []);

  return <div ref={sliderElement} />;
}

здесь я жду, пока будет доступен элемент DOM, а затем создаю экземпляр noUiSlider для этого элемента DOM (div я возвращаю). Я также прикрепляю некоторые обработчики событий внутри эффекта (onUpdate и onEnd).

Использование компонента Slider выглядит следующим образом ...

// custom hook that just sets state
const [min, max, setMinMax] = useSlider(10, 300);

const handleEnd = () => {
  // these are outdated :(
  console.log(min, max);
}

<Slider 
  defaultMin={10} 
  defaultMax={300} 
  step={10} 
  onEnd={handleEnd} 
  onUpdate={setMinMax} 
/>

Проблема, с которой я сталкиваюсь, заключается в том, что внутри handleEnd, когда я хочу сослаться на min и max, они являются начальными значениями, а не значениями, которые я ожидал обновить.

Как получить обновленные значения из одной и той же области видимости из функции, которую я вызываю внутри useEffect?

Я создал codesandbox пример, чтобы показать это в действии.

1 Ответ

3 голосов
/ 06 марта 2019

Основная проблема связана с тем, как вы называете useEffect в вашем Slider компоненте.Вы добавили [] в качестве второго параметра, который предотвращает вызов вашего эффекта более одного раза.Поэтому он вызывается только один раз после первого рендеринга, затем никогда больше, и поэтому он сохраняет старые значения onEnd, содержащие устаревшие значения для минимальных и максимальных значений.

From React's doc :

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

Замените Slider.js этим, и он работает:

import React, { useEffect, useRef } from 'react';
import noUiSlider from 'nouislider';

function Slider(props) {
  const { defaultMin, defaultMax, step } = props;
  const sliderElement = useRef();

  useEffect(() => {
    noUiSlider.create(sliderElement.current, {
      connect: true,
      start: [defaultMin, defaultMax],
      step,
      range: {
        min: [defaultMin],
        max: [defaultMax],
      },
    });

    if (props.onUpdate) {
      sliderElement.current.noUiSlider.on('update', props.onUpdate);
    }
  }, []);

  useEffect(
    () => {
      if (props.onEnd) {
        sliderElement.current.noUiSlider.on('end', props.onEnd);
      }
      return () => {
        sliderElement.current.noUiSlider.off('end');
      };
    },
    [props.onEnd],
  );

  return <div ref={sliderElement} />;
}

export default Slider;

Некоторые замечания здесь:

  • props.onChange никогда не определяется и не используется в вашем Slider компоненте.Я отбросил его.
  • useEffect должен быть разделен на два useEffect s:
    • Один обрабатывает создание слайдера и подключает функцию обновления (которую не нужно обновлять, так какон всегда сохраняет действительную ссылку на setMinMax в useSlider.js)
    • Другое обновление только конечное событие, когда изменяется props.onEnd (что на самом деле ... при каждом рендеринге, см. index.js > handleEnd), Не забудьте очистить прослушиватель событий.
...