React受控输入光标跳转

时间:2017-09-01 12:43:47

标签: reactjs input cursor

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

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

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

7 个答案:

答案 0 :(得分:3)

根据您的问题猜测,您的代码很可能与此类似:

    <input
        autoFocus="autofocus"
        type="text"
        value={this.state.value}
        onChange={(e) => this.setState({value: e.target.value})}
    />

如果使用onBlur处理您的活动,但行为可能会有所不同,但基本上是相同的问题。这里的行为,许多人称之为React“bug”,实际上是预期的行为。

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

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

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

    <input
        autoFocus="autofocus"
        type="text"
        value={this.state.value}
        onChange={(e) => {
            this.cursor = e.target.selectionStart;
            this.setState({value: e.target.value});
        }}
        onFocus={(e) => {
            e.target.selectionStart = this.cursor;
        }}
    />

答案 1 :(得分:2)

这是一个简单的解决方案。为我工作。

<Input
ref={input=>input && (input.input.selectionStart=input.input.selectionEnd=this.cursor)}
value={this.state.inputtext} 
onChange={(e)=>{
this.cursor = e.target.selectionStart;
this.setState({inputtext: e.target.value})
/>

说明:

我们在这里做的是在onChange()中保存光标位置,现在当标签由于状态值变化而重新渲染时,引用代码被执行,在引用代码中我们恢复出光标位置。

答案 2 :(得分:1)

这是我的解决方案:

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;

答案 3 :(得分:0)

我的光标总是跳到行尾。此解决方案似乎可以解决问题(来自github):

Cell1
Cell2 
Cell3
Cell4   <-- barcode using font

答案 4 :(得分:0)

这是我的解决方法

const Input = () => {
    const [val, setVal] = useState('');
    const inputEl = useRef(null);

    const handleInputChange = e => {
      const { value, selectionEnd } = e.target;
      const rightCharsCount = value.length - selectionEnd;
      const formattedValue = parseInt(value.replace(/\D/g, ''), 10).toLocaleString();
      const newPosition = formattedValue.length - rightCharsCount;

      setVal(formattedValue);

      setTimeout(() => {
        inputEl.current.setSelectionRange(newPosition, newPosition);
      }, 0);
    };

    return <input ref={inputEl} value={val} onChange={handleInputChange} />;
};

答案 5 :(得分:0)

// Here is a custom hook to overcome this problem:

import { useRef, useCallback, useLayoutEffect } from 'react'
/**
 * This hook overcomes this issue {@link https://github.com/reduxjs/react-redux/issues/525}
 * This is not an ideal solution. We need to figure out why the places where this hook is being used
 * the controlled InputText fields are losing their cursor position when being remounted to the DOM
 * @param {Function} callback - the onChangeCallback for the inputRef
 * @returns {Function} - the newCallback that fixes the cursor position from being reset
 */
const useControlledInputOnChangeCursorFix = callback => {
  const inputCursor = useRef(0)
  const inputRef = useRef(null)

  const newCallback = useCallback(
    e => {
      inputCursor.current = e.target.selectionStart
      if (e.target.type === 'text') {
        inputRef.current = e.target
      }
      callback(e)
    },
    [callback],
  )

  useLayoutEffect(() => {
    if (inputRef.current) {
      inputRef.current.setSelectionRange(inputCursor.current, inputCursor.current)
    }
  })

  return newCallback
}

export default useControlledInputOnChangeCursorFix

// Usage:

import React, { useReducer, useCallback } from 'react'
import useControlledInputOnChangeCursorFix from '../path/to/hookFolder/useControlledInputOnChangeCursorFix'

// Mimics this.setState for a class Component
const setStateReducer = (state, action) => ({ ...state, ...action })

const initialState = { street: '', address: '' }

const SomeComponent = props => {
  const [state, setState] = useReducer(setStateReducer, initialState)

  const handleOnChange = useControlledInputOnChangeCursorFix(
    useCallback(({ target: { name, value } }) => {
      setState({ [name]: value })
    }, []),
  )

  const { street, address } = state

  return (
    <form>
      <input name='street' value={street} onChange={handleOnChange} />
      <input name='address' value={address} onChange={handleOnChange} />
    </form>
  )
}

答案 6 :(得分:0)

我尝试了上述所有解决方案,但没有一个对我有用。相反,我更新了 e.currentTarget.selectionStart React 合成事件类型上的 e.currentTarget.selectionEndonKeyUp。例如:

const [cursorState, updateCursorState] = useState({});
const [formState, updateFormState] = useState({ "email": "" });

const handleOnChange = (e) => {
    // Update your state & cursor state in your onChange handler
    updateCursorState(e.target.selectionStart);
    updateFormState(e.target.value);
}

<input
    name="email"
    value={formState.email}
    onChange={(e) => handleOnChange(e)}
    onKeyUp={(e) => {
        // You only need to update your select position in the onKeyUp handler:
        e.currentTarget.selectionStart = cursorState.cursorPosition;
        e.currentTarget.selectionEnd = cursorState.cursorPosition;
    }}
/>

另外,请注意 selectionStartselectionEnd 获取器在 email 类型的输入字段中不可用。