我正在尝试使用'静态'模式创建输入组件。在此模式下,输入将被包含输入值的跨度替换。在这个问题中,我使用范围输入。
import React from 'react';
import ReactDOM from 'react-dom';
const { useState, useEffect, } = React;
const RangeInput = ({
inputID, min, max, defaultValue, children,
}) => {
const [valueAfterStaticChange, setValueAfterStaticChange] = useState(
defaultValue
);
const [isStatic, setIsStatic] = useState(false);
useEffect(
() => () => {
// do this before component unmounts and when 'isStatic' changes,
// but only when 'isStatic' is true
isStatic
|| setValueAfterStaticChange(document.getElementById(inputID).value);
},
[isStatic]
);
return (
<div>
<label htmlFor={inputID}>{children}</label>
<span style={{ display: isStatic || 'none', }}>
{valueAfterStaticChange}
</span>
<input
type="range"
min={min}
max={max}
defaultValue={valueAfterStaticChange}
id={inputID}
style={{ display: isStatic && 'none', }}
/>
<button
onClick={() => {
setIsStatic(!isStatic);
}}
>
Toggle Static
</button>
</div>
);
};
ReactDOM.render(
<RangeInput inputID="myID" min={0} max={100} defaultValue={50}>
My Range Input
</RangeInput>,
document.getElementById('root')
);
上述代码有效。但是它使用display: none
模拟"无渲染"。我认为我可以通过以下逻辑完成实际卸载:
// all unchanged until...
return (
<div>
<label htmlFor={inputID}>{children}</label>
{isStatic ? (
<span>{valueAfterStaticChange}</span>
) : (
<input
type="range"
min={min}
max={max}
defaultValue={valueAfterStaticChange}
id={inputID}
/>
)}
<button
onClick={() => {
setIsStatic(!isStatic);
}}
>
Toggle Static
</button>
</div>
);
};
以某种方式我的useEffect
返回函数实际上删除了输入,因此无法正确读取输入。我认为在useEffect
中使用返回函数将导致一个函数,该功能将在组件卸载之前运行。我在做什么错?
自然,如果未安装<input>
元素,则无法访问document.getElementById(inputID).value
- 它不存在!甚至这样,您也不想以这种方式阅读其价值。这是一种非常反应的做事方式。React组件应该具有他们在状态和道具中所需的一切 - 如果您发现自己在直接询问或直接在DOM上行动,那么您(几乎总是(走下了一条黑暗的道路。
而不是您想要的是受控组件。
受控组件不依赖DOM来存储像"值"之类的东西。相反,该组件仅显示在状态中具有的任何值并提供方便的事件挂钩。
我强烈建议您阅读我链接的文档,看看您是否可以自己找出实现。毕竟这是学习的最佳方法!但是,如果您仍然无法弄清楚,我会回来以一个基本的工作示例编辑答案。
编辑:这是一个简单的工作演示,并带有一些评论。
https://stackblitz.com/edit/contreolled-input-demo?file= index.js
请注意,这实际上很简单!<App>
跟踪状态下的滑块值,而<RangeInput>
只是将该值作为Prop消耗,并提供了一个事件处理程序,用于将其更新为<App>
。<RangeSlider>
可以跟踪其自己的"编辑模式"值,因为其他组件可能不需要访问它(但是您可以在此处"提升"该级别!(您甚至在这里不需要useEffect()
!
,除非有理由在卸载时获得值,否则您似乎只能跟踪其变化的值。这样,您根本不需要useEffect
:
const RangeInput = ({ inputID, min, max, defaultValue, children }) => {
const [value, setValue] = useState(defaultValue); // <-- keep track of value
const [isStatic, setIsStatic] = useState(false);
return (
<div>
<label htmlFor={inputID}>{children}</label>
{isStatic ? (
<span>{value}</span>
) : (
<input
type="range"
min={min}
max={max}
defaultValue={value}
onChange={e => setValue(e.target.value)} // <-- Set value onChange
id={inputID}
/>
)}
<button
onClick={() => {
setIsStatic(!isStatic);
}}
>
Toggle Static
</button>
</div>
);
};