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 中,当我想引用 minmax 时,它们是初始值而不是我期待更新。

如何从我在 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)。 不要忘记清理事件侦听器。