为什么我的嵌套组件会自行重建?

Why is my nested component rebuilding itself?

我正在尝试基于原生 input/range 元素构建自定义滑块。汇总代码如下所示:

const Slider = ({ className, backgroundColor, ...inputAttributes }) => {
  const [value, setValue] = useState(5);

  const handleSliderChange = event => {
    setValue(event.currentTarget.value);
  };

  const SliderElement = () => (
    <input value={value} onChange={handleSliderChange} {...inputAttributes} />
  );

  // ...

  return (
    <>
      <SliderElement />
      <p>
        <Display>{value}</Display>
      </p>
    </>
  );
};

它的名字是这样的:

<Slider type="range" min={1} max={10} step={1} />

Full running code here: https://codesandbox.io/s/000qm47pjw

w/ nested SliderElement component

使用上面的代码,我一次只能移动滑块一步(我必须 "unclick" 滑块并再次单击它才能进行下一步)。我怀疑这是因为每次移动滑块时 SliderElement 都会重新渲染。

然而...

如果我完全跳过 SliderElement 并将 <input... 直接放入返回的 JSX 中,它会完美运行。

Full running code here: https://codesandbox.io/s/5469y582yn

w/o nested SliderElement component


为什么 SliderElement 每次使用它时都会重新渲染(假设是这种情况)?

如何让 <input... 包裹在嵌套组件中,并且仍然能够提供黄油般流畅的体验?

区分这两个:

var aReactElement = <input />
var aReactComponent = () => <input />

一个反应组件负责决定是否应该重新渲染它管理的反应元素。它在一个前提下工作,组件对象 instance 应该保持不变,以便作为一个持久状态的引用键。

在您的示例中,SliderElement 实例在 Slider 的每次重新渲染中不断变化,每次都是一个新函数。因此,您的 DOM 树不断卸载然后安装 <input /> 元素。

您可以将其设为元素,而不是将 SliderElement 设为组件。

const SliderElement = (
    <input value={value} onChange={handleSliderChange} {...inputAttributes} />
  );

  // ...

  return (
    <>
      {SliderElement}
      <p>
        <Display>{value}</Display>
      </p>
    </>
  );