1

I use numeral to format my numbers from 4500000 to 4 500 000. The problem comes with cursor position - it gets wrong when the number of spaces is changing. I tried to save cursor position when my input is change, but something is wrong with it.

import React from "react";
import { render } from "react-dom";
import numeral from "numeral";

class App extends React.Component {
    pricePosition = 0;
    priceInput = React.createRef();

    constructor(props) {
        super(props);

        numeral.localeData().delimiters.thousands = " ";

        this.state = {
            price: 4500000 // comes from props
        };
    }

    componentDidUpdate() {
        this.priceInput.current.selectionStart = 
        this.priceInput.current.selectionEnd = this.pricePosition; //tried like this
    }

    handleInputChange = field => ev => {
        this[`${field}Position`] = Number(ev.target.selectionEnd);
        this.setState({
            [field]: Number(ev.target.value.replace(/\D/g, ""))
        });
    };

    render() {
        return (
            <div>
                Price here:
                    <input
                        ref={this.priceInput}
                        value={numeral(this.state.price).format("0,0")}
                        onChange={this.handleInputChange("price")}
                    />
            </div>
        );
    }
}

render(<App />, document.getElementById("root"));

Also you can check it: codesandbox

Mad Max
  • 283
  • 1
  • 7
  • 19
  • Seems like it's off-by-1? Anyway it seems to work even with your `componentDidUpdate` removed. Where do you want the cursor to be? – Roy Wang Apr 27 '18 at 06:35
  • For example when I delete `5` from my input my cursor position is in wrong place. I suppose, because of number of spaces in value(from 2 to 1). And it doesn't work without `componentDidUpdate` – Mad Max Apr 27 '18 at 06:50

2 Answers2

0

You have to rewrite componentDidUpdate like below in order to make it work (the ugly way):

    componentDidUpdate() {
        this.priceInput.current.selectionEnd = this.pricePosition + 1; //tried like this
    }

The point is, you are only formatting the value to be shown on the screen, not the state itself, that's why the cursor doesn't match the actual length.

I suggest that you rewrite your code like below:

import React from "react";
import { render } from "react-dom";
import numeral from "numeral";

class App extends React.Component {
    priceInput = React.createRef();

    constructor(props) {
        super(props);

        numeral.localeData().delimiters.thousands = " ";

        this.state = {
            price: 4500000 // comes from props
        };
    }

    handleInputChange = field => ev => {
        const formattedValue = numeral(ev.target.value).format("0,0");
        this[`${field}Position`] = Number(formattedValue.length);
        this.setState({
            [field]: Number(ev.target.value.replace(/\D/g, ""))
        });
    };

    render() {
        return (
            <div>
                Price here:
                    <input
                        ref={this.priceInput}
                        value={numeral(this.state.price).format("0,0")}
                        onChange={this.handleInputChange("price")}
                    />
            </div>
        );
    }
}

render(<App />, document.getElementById("root"));

You can now get rid of the position variable.

Hope this help ;)

Trung Tran
  • 101
  • 6
0

I got this kind of hacky solution:

import React from "react";
import { render } from "react-dom";
import numeral from "numeral";

class App extends React.Component {
  constructor(props) {
    super(props);

    numeral.localeData().delimiters.thousands = " ";

    this.state = {
      inFocus: false,
      price: 4500000 // comes from props
    };
  }

  changeFocus = () => {
    this.setState(prevState => ({
      inFocus: !prevState.inFocus
    }));
  };

  handleInputChange = field => ev => {
    this.setState({
      [field]: Number(ev.target.value.replace(/\D/g, ""))
    });
  };

  render() {
    const { price, inFocus } = this.state;
    return (
      <div>
        Price here:
        <input
          value={inFocus ? price : numeral(price).format("0,0")}
          onFocus={this.changeFocus}
          onBlur={this.changeFocus}
          onChange={this.handleInputChange("price")}
        />
      </div>
    );
  }
}

render(<App />, document.getElementById("root"));

When input is in focus we are using unformat value.

codesandbox

Mad Max
  • 283
  • 1
  • 7
  • 19