2

I may be thinking about this wrong but I would like to have two components, a parent and a child, where the child has an input element where the user is expected to enter a number. The parent component's callback will only get called on valid numbers. This is enforced via Typescript and parseFloat.

class Child extends React.Component {
  state = { number: '' };
  inputChanged = (ev) => {
    const stringy = ev.target.value;
    this.setState({number: stringy});
    const parsed = parseFloat(stringy);
    if (parsed) {
      this.props.numberChanged(parsed);
    }
  }
  render() {
    return <input
      type="text"
      value={this.props.number || this.state.number}
      onChange={this.inputChanged}
    />
  }
}

class Container extends React.Component {
  state = { number: 5 };
  numberSet = (value: number) => {
    this.setState({number: value});
  }
  render() {
    return <Child
      number={this.state.number}
      numberChanged={this.numberSet}
    />;
  }
}

The challenge comes in that I want to be able to set and change the number value in the child component with props. But I also want to allow the user to type numbers in.

If I only rely on the props and not state then when they type things like decimal points they do not show up because they are invalid from the perspective of parseFloat. But if I only rely on state then I can't seem to get the parent component to update the child component with new props.

I have created the following CodePen to reproduce as well. Notice how you cannot type a decimal point in the input field. Where am I going wrong here?

2
  • 1
    This is enforced via Typescript and parseFloat - TypeScript has no bearing on your code once it is running. ...decimal points they do not show up because they are invalid from the perspective of parseFloat - what? Commented Feb 3, 2019 at 9:04
  • @ethane maybe "enforced" was the wrong word choice, but what I was trying to get across is that I want my upstream state to only deal with numbers, not strings. The child component is using parseFloat to do the conversion from the input's value Commented Feb 3, 2019 at 16:35

1 Answer 1

1

You are complicating things here with the pattern. The question itself may be incorrect and mis-informed.

But to address your problem of not able to use decimal values, the problem is parseFloat('52.') is 52 and the code runs on every change because of the change in this.props and you are never successful in adding that '.' !

Using a controlled component like below should solve all your problems!

class Child extends React.Component {
    render() {
       return <input
                type="text"
                value = {this.props.number}
                onChange={this.props.numberChanged}
              />
       }
}

class Container extends React.Component {
   state = { number: 5 };
   numberSet = (ev) => {
       let value = event.target.value;
       this.setState({number: value});
   }
   render() {
      return <Child
              number={this.state.number}
              numberChanged={this.numberSet}
             />;
   }
}

React.render(<Container />, document.getElementById('app'));`
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for the response! I definitely feel like I am complicating it. The problem with this approach though, at least in my application, is that now the container's state will have non-numeric values for number when the user is "mid-typing" or right after the decimal point is pressed. That's what I was hoping to avoid. To be more specific, the state interface for the Container component has a "number" type in Typescript
Hi @Matt, Typescript is only a static type checking language that helps you catch errors early before compiling down to regular JavaScript. Be rest assured, the code in production is good old JavaScript with no type checking systems in place. The problem with preventing 'mid-typing' issues is that you can never type a decimal number like the issue you mentioned. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.