Я работаю над созданием компонента для внешней библиотеки, которую хочу использовать в своем проекте (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.info(min, max);
}
<Slider
defaultMin = {10}
defaultMax = {300}
step = {10}
onEnd = {handleEnd}
onUpdate = {setMinMax}
/>
Проблема, с которой я сталкиваюсь, заключается в том, что внутри handleEnd, когда я хочу сослаться на min и max, они являются начальными значениями, а не значениями, которые я ожидал обновить.
Как мне получить обновленные значения из той же области видимости из функции, которую я вызываю внутри useEffect?
Я создал пример кодыпесочница, чтобы показать это в действии.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Основная проблема связана с тем, как вы вызываете useEffect в своем компоненте Slider. Вы добавили [] в качестве второго параметра, что предотвращает вызов вашего эффекта более одного раза. Таким образом, он просто вызывается один раз после первого рендеринга, а затем никогда больше, и поэтому он сохраняет старое onEnd, сохраняя устаревшие значения для минимума и максимума.
Из документ React :
If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run.
Замените 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:
setMinMax в useSlider.js)props.onEnd (что на самом деле... при каждом рендере, см. index.js > handleEnd). Не забудьте очистить прослушиватель событий.Тогда у вас другие проблемы (я проверю), но это точно правильный старт.
Спасибо за мысль @Strebler. К сожалению, у меня он рендерится только один раз, потому что в противном случае он будет повторно создавать экземпляр noUiSlider каждый раз, когда он повторно рендерится. Я попытался поместить свои реквизиты в этот массив (т. е. [props.onEnd, props.onUpdate]), но, поскольку сами реквизиты этих функций не обновляются, это по сути то же самое, что и [].
props.onEnd действительно обновляется, поскольку вы воссоздаете функцию при каждом рендеринге в index.js. Я пытаюсь разделить ваше useEffect на два useEffect. Давайте посмотрим, куда это пойдет.
@Strebler Блоки if (!sliderElement.current) не нужны ни в useEffect.
@ Адам Хорошо, я просто сохранил исходный код с этими проверками. Я отредактирую для более чистого ответа.
Я заметил, что это решение вызывает экспоненциальное срабатывание событий при каждом изменении ползунка, и в конечном итоге конечное значение не обновляется. Это может быть проблемой с пакетом noUiSlider, который использует OP.
Да только что тоже самое видел. Я готов.
@ Адам Исправлено. Просто нужно было очистить событие end во втором onEffect. Проверьте мое обновление.
Это ломает приложение.