#javascript #reactjs #react-hooks
#javascript #reactjs #реагирующие перехваты
Вопрос:
Я работаю над созданием компонента для внешней библиотеки, которую я хочу использовать в своем проекте (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:
Основная проблема возникает из-за того, как вы вызываете useEffect
в своем Slider
компоненте. Вы добавили []
в качестве второго параметра, который не позволяет вызывать ваш эффект более одного раза. Таким образом, он вызывается только один раз после первого рендеринга, а затем никогда больше, и поэтому он сохраняет старое onEnd
, сохраняя устаревшие значения для min и max.
Из документа React :
Если вы хотите запустить эффект и очистить его только один раз (при монтировании и размонтировании), вы можете передать пустой массив ([]) в качестве второго аргумента. Это сообщает React, что ваш эффект не зависит ни от каких значений из props или state, поэтому его никогда не нужно запускать повторно.
Замените 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
) - Другой обновляет только событие end, когда
props.onEnd
изменяется (что на самом деле … при каждом рендеринге, см.index.js > handleEnd
). Не забудьте очистить прослушиватель событий.
- Один обрабатывает создание слайдера и подключает функцию обновления (которую не нужно обновлять, поскольку она всегда сохраняет действительную ссылку на
Комментарии:
1. Это прерывает работу приложения.
2. Тогда у вас возникнут другие проблемы (я проверю), но это, безусловно, правильное начало.
3. Спасибо за мысль @Strebler. К сожалению, у меня он отображается только один раз, потому что в противном случае он повторно создавал бы экземпляр noUiSlider каждый раз при повторном отображении. Я попытался поместить свои реквизиты в этот массив (т. Е.
[props.onEnd, props.onUpdate]
), но поскольку сами эти функциональные реквизиты не обновляются, это, по сути, то же самое, что[]
.4.
props.onEnd
действительно обновляется, поскольку вы воссоздаете функцию при каждом рендеринге в index.js . Я пытаюсь разделить ваш файлuseEffect
на двеuseEffect
части. Давайте посмотрим, к чему это приведет.5. @Strebler
if (!sliderElement.current)
Блоки не нужны ни в том, ни в другомuseEffect
случае.