I'm getting a floating number from sqlite db and setting up it in redux state. Than I'm showing this property to TextInput component. To do this, I have to convert floating number to string. While editing value, I trigger event onChangeText, convert string to floating number and update redux state.
When I clear last char after point in a TextInput, my point also clearing because of converting property value from number to string. How can I save point in this case? And what's the wright way to work with floating values in react-redux?
My custom component code:
import React from 'react';
import PropTypes from 'prop-types';
import { View, TextInput } from 'react-native';
class FormFieldNumeric extends React.PureComponent {
constructor() {
super();
this.state = {
textValue: ''
}
}
componentWillReceiveProps(nextProps, nextContext) {
if (parseFloat(this.state.textValue) !== nextProps.value) {
this.setState({
textValue: nextProps.value ? String(nextProps.value) : ''
})
}
}
onChangeText = text => {
if (!text) {
this.changeValue('');
return;
}
if (text.length === 1) {
if (!'0123456789'.includes(text)) {
return;
}
}
const lastSymbol = text[text.length - 1];
if ('1234567890.,'.includes(lastSymbol)) {
if (text.split('').filter(ch => ch === '.' || ch === ',').length > 1) {
return;
}
if (lastSymbol === ',') {
this.changeValue(this.state.textValue + '.');
return;
}
this.changeValue(text);
}
};
changeValue = text => {
this.setState({
textValue: text
});
this.props.onChange(text ? parseFloat(text) : 0);
};
render() {
const { caption, value, onChange, placeholder } = this.props;
return (
<View>
<TextInput
value={this.state.textValue}
keyboardType="numeric"
onChangeText={this.onChangeText}
placeholder={placeholder}
maxLength={10}
/>
</View>
)
}
}
FormFieldNumeric.propType = {
placeholder: PropTypes.string,
value: PropTypes.number,
onChange: PropTypes.func.isRequired
};
export default FormFieldNumeric;
Understood what was my mistake. I fell into the anti-pattern trap when I try to keep the same state in two places. This article describes in detail this. According to the recommendations from the article, I used an uncontrolled component and stored the state directly in the component, and I only pass the property in order to initialize the value.
import React from 'react';
import PropTypes from 'prop-types';
import { View, TextInput } from 'react-native';
class FormFieldNumeric extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
defaultValue: props.defaultValue,
textValue: this.floatToTextValue(props.defaultValue)
}
}
floatToTextValue = value => {
return value ? String(value) : '';
};
render() {
const { placeholder } = this.props;
return (
<View>
<TextInput
value={this.state.textValue}
keyboardType="numeric"
onChangeText={this.onChangeText}
placeholder={placeholder}
/>
</View>
)
}
}
FormFieldNumeric.defaultValue = {
placeholder: '',
defaultValue: 0
};
FormFieldNumeric.propType = {
placeholder: PropTypes.string,
defaultValue: PropTypes.number,
onChange: PropTypes.func.isRequired
};
export default FormFieldNumeric;
And for the component to update the values after loading the redux state, I made the _isLoading field in the parent component, which is true by default, but becomes false after the data is loaded. I passed this value as the key property of the parent component:
class ParentComponent extends React.PureComponent {
_isLoading = false;
async componentDidMount() {
await this.props.onCreate();
this._isLoading = false;
}
render() {
return (
<View key={this._isLoading}>
<FormFieldNumeric
defaultValue={this.props.cashSum}
onChange={this.onChangeCashSum}
placeholder="0.00"
/>
</View>
)
}
}