反应控制的输入光标跳转

RMT*_*RMT 5 input cursor reactjs

我正在使用React并格式化了一个受控的输入字段,当我写一些数字并在输入字段之外单击时,该输入字段可以正常工作。但是,当我要编辑输入时,光标跳到输入字段中值的前面。这仅发生在IE中,而不发生在例如Chrome中。我已经看到对于某些程序员,光标会跳到该值的后面。因此,我认为我的光标跳到最前面的原因是因为该值在输入字段中向右对齐而不是向左对齐。这是一个senario:

我的第一个输入是1000然后我想将其编辑为10003,但结果是31000

有没有办法控制光标不跳?

Dan*_*ton 35

这是标签的直接替代品<input/>。这是一个简单的功能组件,使用钩子来保留和恢复光标位置:

import React, { useEffect, useRef, useState } from 'react';

const ControlledInput = (props) => {
   const { value, onChange, ...rest } = props;
   const [cursor, setCursor] = useState(null);
   const ref = useRef(null);

   useEffect(() => {
      const input = ref.current;
      if (input) input.setSelectionRange(cursor, cursor);
   }, [ref, cursor, value]);

   const handleChange = (e) => {
      setCursor(e.target.selectionStart);
      onChange && onChange(e);
   };

   return <input ref={ref} value={value} onChange={handleChange} {...rest} />;
};

export default ControlledInput;
Run Code Online (Sandbox Code Playgroud)

...或者如果您愿意的话可以使用TypeScript

import React, { useEffect, useRef, useState } from 'react';

type InputProps = React.ComponentProps<'input'>;
const ControlledInput: React.FC<InputProps> = (props) => {
   const { value, onChange, ...rest } = props;
   const [cursor, setCursor] = useState<number | null>(null);
   const ref = useRef<HTMLInputElement>(null);

   useEffect(() => {
      ref.current?.setSelectionRange(cursor, cursor);
   }, [ref, cursor, value]);

   const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
      setCursor(e.target.selectionStart);
      onChange?.(e);
   };

   return <input ref={ref} value={value} onChange={handleChange} {...rest} />;
};

export default ControlledInput;
Run Code Online (Sandbox Code Playgroud)

  • 这太美了,你是一个传奇。我在这里尝试了其他答案,但这不仅确实有效,而且工作得很好(没有光标跳跃然后移回,非常流畅的用户体验),而且它非常易于使用! (6认同)
  • 请注意,`input.setSelecetionRange()` 不适用于 `type="number"` - 如果这就是您想要的,我发现我必须 `type="text" inputMode="numeric"`。 (4认同)
  • 感谢您的代码。我建议的唯一更改是将“useEffect”挂钩更改为“useLayoutEffect”挂钩,否则用户可能会看到光标从末尾跳到“selectionStart”位置。我尝试在[此处]解释一种不同的方法(https://github.com/Atri-Labs/til/blob/main/react/control-input.md) (4认同)
  • 谢谢,这对我也有用。我正在使用带有打字稿的“textarea”,所以我必须[更改代码](/sf/answers/4938443471/)一点:) (2认同)

小智 6

This is my solution:

import React, { Component } from "react";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: ""
    };

    //get reference for input
    this.nameRef = React.createRef();

    //Setup cursor position for input
    this.cursor;
  }

  componentDidUpdate() {
    this._setCursorPositions();
  }

  _setCursorPositions = () => {
    //reset the cursor position for input
    this.nameRef.current.selectionStart = this.cursor;
    this.nameRef.current.selectionEnd = this.cursor;
  };

  handleInputChange = (key, val) => {
    this.setState({
      [key]: val
    });
  };

  render() {
    return (
      <div className="content">
        <div className="form-group col-md-3">
          <label htmlFor="name">Name</label>
          <input
            ref={this.nameRef}
            type="text"
            autoComplete="off"
            className="form-control"
            id="name"
            value={this.state.name}
            onChange={event => {
              this.cursor = event.target.selectionStart;
              this.handleInputChange("name", event.currentTarget.value);
            }}
          />
        </div>
      </div>
    );
  }
}

export default App;

Run Code Online (Sandbox Code Playgroud)


小智 6

我知道 OP 已经有 5 年历史了,但有些人仍然面临着同样的问题,而且这个页面在 Google 搜索上有很高的知名度。尝试替换:

<input value={...}
Run Code Online (Sandbox Code Playgroud)

<input defaultValue={...}
Run Code Online (Sandbox Code Playgroud)

这将解决我在那里见过的大多数情况。


Ade*_*lal 5

通过您的问题来猜测,您的代码很可能类似于以下内容:

    <输入
        autoFocus =“ autofocus”
        type =“ text”
        值= {this.state.value}
        onChange = {(e)=> this.setState({value:e.target.value})}
    />

如果处理事件onBlur但本质上是相同的问题,则行为可能会有所不同。实际上,这是预期的行为,许多人已将其称为React的“错误”。

输入控件的值不是控件在加载时的初始值,而是value绑定到的基础this.state。当状态改变时,控件由React重新呈现。

本质上,这意味着该控件由React重新创建并由状态值填充。问题在于,它无法在重新创建之前知道光标位置。

我发现可以解决此问题的一种方法是在重新渲染之前记住光标位置,如下所示:

    <输入
        autoFocus =“ autofocus”
        type =“ text”
        值= {this.state.value}
        onChange = {(e)=> {
            this.cursor = e.target.selectionStart;
            this.setState({value:e.target.value});
        }}
        onFocus = {(e)=> {
            e.target.selectionStart = this.cursor;
        }}
    />

  • 这并不完全正确。简单地用 `&lt;input value={...} onChange={...} /&gt;` 控制输入不会导致光标跳转。如果你的值是由状态控制的,React 将保持输入的光标位置。问题是当输入接收到更改事件并且值“不”立即更改时。就我而言,这是因为输入值是通过 chrome 存储 API(它是一个扩展)异步更改的。对于用户来说,它看起来是瞬时的,但实际上它是异步的,因此 React 不知道该值会有所不同。 (12认同)
  • 如何用钩子来做呢? (2认同)