React useEffect 挂钩引用不正确的值
React useEffect hook referencing incorrect values
我正在为我想在我的项目中使用的外部库创建一个组件 (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 元素可用,然后在该 DOM 元素上创建 noUiSlider 实例 (div
i return)。我还在效果中附加了一些事件处理程序(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 示例来展示这一点。
主要问题来自您在 Slider
组件中调用 useEffect
的方式。您添加了 []
作为第二个参数,这可以防止您的效果被多次调用。所以它只在第一次渲染后被调用一次,然后再也不会,所以它保留旧的 onEnd
,保存最小值和最大值的过时值。
来自 React's doc :
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
:
- 一个处理滑块创建和连接更新函数(不需要更新,因为它始终在
useSlider.js
中保持对 setMinMax
的有效引用)
- 另一个只更新结束事件,当
props.onEnd
发生变化时(实际上...在每个渲染器上,请参阅 index.js > handleEnd
)。 不要忘记清理事件侦听器。
我正在为我想在我的项目中使用的外部库创建一个组件 (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 元素可用,然后在该 DOM 元素上创建 noUiSlider 实例 (div
i return)。我还在效果中附加了一些事件处理程序(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 示例来展示这一点。
主要问题来自您在 Slider
组件中调用 useEffect
的方式。您添加了 []
作为第二个参数,这可以防止您的效果被多次调用。所以它只在第一次渲染后被调用一次,然后再也不会,所以它保留旧的 onEnd
,保存最小值和最大值的过时值。
来自 React's doc :
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
:- 一个处理滑块创建和连接更新函数(不需要更新,因为它始终在
useSlider.js
中保持对setMinMax
的有效引用) - 另一个只更新结束事件,当
props.onEnd
发生变化时(实际上...在每个渲染器上,请参阅index.js > handleEnd
)。 不要忘记清理事件侦听器。
- 一个处理滑块创建和连接更新函数(不需要更新,因为它始终在