我有简单的组件
class ContentEditable extends React.Component {
constructor(props) {
super(props);
this.handleInput = this.handleInput.bind(this);
}
handleInput(event) {
let html = event.target.innerHTML;
if (this.props.onChange && html !== this.lastHtml) {
this.props.onChange({ target: { value: html, name: this.props.name } });
this.lastHtml = html;
}
}
render() {
return (
<span
contentEditable="true"
onInput={this.handleInput}
className={"auto " + this.props.className}
dangerouslySetInnerHTML={{ __html: this.props.value }}
/>
);
}
}
export default ContentEditable;
<ContentEditable
value={this.state.name}
onChange={e => {
this.setState({ name: e.target.value });
}}
/>;
组件可以工作,但光标位置永远不会改变,它始终位于第一个位置,而不是渲染文本之后。
我测试了这个论坛的示例,但它对我不起作用。
我使用
React 15.6.1
并在 chrome (Os X)
上进行测试。
有什么提示我可以解决这个问题吗?
带有
useRef
的解决方案如下所示。
const ContentEditableWithRef = (props) => {
const defaultValue = useRef(props.value);
const handleInput = (event) => {
if (props.onChange) {
props.onChange(event.target.innerHTML);
}
};
return (
<span
contentEditable
onInput={handleInput}
className="custom-textarea"
dangerouslySetInnerHTML={{ __html: defaultValue.current }}
/>
);
};
完整的代码和盒子可用这里,用它来看看它是如何工作的!
代码沙箱示例包含两个组件
- 一个是
,它解决了ContentEditableWithRef
的问题,这是一个不受控制的组件,并且useRef
- 另一个组件是
,它使用ContentEditable
来解决同样的问题。useState
这里
useRef
将保持默认值/初始值与组件渲染周期分开,因此它将保留原始值,而不会受到我们在React组件中执行的其他类型操作的影响。
这个组件做了两件事
onChange
方法将用户输入发送到父组件prop
的默认值,并在自定义输入框中呈现该值(使用 value
创建)
contentEditable
修复即可。只需将 event.target 的
ref
分配给 textContent
。ref
所以我尝试了@rufatZZ解决方案,它解决了我的第二个问题(从父级更新值),但没有解决丢失光标位置的问题。
所以我混合了两种解决方案,主要是@rufatZZ解决方案,但使用@Lakshmaji提出的带有
const textareaEl = useRef<HTMLDivElement>(null);
const handleChange = (e: React.ChangeEvent<HTMLDivElement>) => {
textareaEl.current.textContent = e.target.textContent;
onChange(e); // If you have change event for form/state
};
/** If you're passing value from state,
you can mutate it each change for not losing cursor position.
*/
useEffect(() => {
if (value) {
textareaEl.current.textContent = value;
}
}, [value]);
return (
<div
id="textarea-element"
ref={textareaEl}
contentEditable={true}
suppressContentEditableWarning={true}
onChange={handleChange}
/>
)
的值参考,它对我有用。
这里:dangerouslySetInnerHTML
// this was from @Lakshmaji and keeps me the cursor position
const defaultValue = useRef(value);
// this was from @rufatZZ and allows me to keep the value even with updates from the parent
const textAreaElement = useRef<HTMLDivElement>(null);
/** If you're passing value from state,
you can mutate it each change for not losing cursor position.
*/
useEffect(() => {
if (value && textAreaElement.current) {
textAreaElement.current.textContent = value;
}
}, [value]);
function handleInputChange(event) {
if (textAreaElement.current) {
textAreaElement.current.textContent = event.currentTarget.innerText;
}
// propagate the changed value to the parent
onChange?.(event.currentTarget.innerText);
}
return (
<span
contentEditable
ref={textAreaElement}
suppressContentEditableWarning
onInput={handleInputChange}
dangerouslySetInnerHTML={{ __html: defaultValue.current }}
/>
);
和
useRef
的一个
dangerouslySetInnerHTML
如果您使用占位符或类似的内容,请使用
function GrowingTextArea() {
const [value, setValue] = useState('')
return (
<span
role="textbox"
contentEditable
style={{ whiteSpace: 'pre-wrap' }}
onBlur={(event) => setValue(event.currentTarget.innerText)}
>
{value}
</span>
)
}
CSS 选择器
添加此内容
:empty