Form input very slow / delayed when typing - javascript

I have a form comprised of 2 objects - A and B both objects have around 20 variables in them.
When it type there is a couple of seconds delay before the state is updated with the value.
My state is along the lines of this where the data is passed in from a parent component:
export class Booking extends Component<any, BookingProps & BookingState> {
constructor(bookingProps: BookingProps) {
super(bookingProps);
this.state = {
a: bookingProps.a,
b: bookingProps.b,
errors: {}
... around six other objects for modal, default data etc
};
}
Many of the input form form fields are like this:
<Form.Field>
<label htmlFor="ref">Ref</label>
<input
placeholder="Ref"
value={this.state.a.ref}
id="ref"
onChange={this.handleBookingFieldChange}
style={{ backgroundColor: 'lightgrey' }}
/>
</Form.Field>
In the other form fields part of state.b it is exactly the same except the value is this.state.b.ref and the onChange is onChange={this.handleBookingExtrasChange}.
So essentially i have separate onChange handlers for state a and b:
private handleBookingFieldChange = (e: any) => {
const key = e.target.id;
const value = e.target.value;
this.setState({
booking: { ...this.state.a, [key]: value }
});
};
So when i type it takes around 3 seconds for the letters to appear in the text field. I'm new to React (from Angular) and can't think why this would take so long to display what i typed.
I would guess that the state is updating every field but i'm not sure. What can i do to speed it up?

I found that by switching the input a little will work as you type - essentially setting defaultValue instead ofvalue and using onBlur instead of onChange. Now the form fields update when typing:
<Form.Field>
<label htmlFor="ref">Ref</label>
<input
placeholder="Ref"
defaultValue={this.state.a.ref}
id="ref"
onBlur={this.handleBookingFieldChange}
type={'number'}
/>
</Form.Field>

Related

REACT-HOOKS: How do I store a modifed parameter made by the user?

I have checkboxes and I want to save in useState hooks the modified value made by the user. By default the current state is fixed and the checkbox is filled if my_value === 1, elif 0 unfilled. But if my user decides to uncheck it, how can I store this action. (if unchecked the value is 0).
Same idea with dropdown, the default value is fixed. The user can change the Taste( Good/Medium/Bad)or the Comments ((0/4....4/4)).
For now I get only the current state.
export default function Display() {
...
//For my checkboxes
const [availability, setAvailability] = useState(item.values[0].availability)
...
const [trust, setTrust] = useState(item.values[0].trust)
//For my dropdowns
const [taste, setTaste] = useState(item.taste)
...
const [comments, setComments] = useState(rule.comments)
function Checkbox({ value }) {
const [checked, setChecked] = useState(value);
return (
<label>
<input
type="checkbox"
checked={checked}
onChange={() => setChecked(checked => !checked)}
/>
{value}
</label>
);
}
return (
<div>
<div>
Availability : <Checkbox value={!!availability} />
</div>
....
<div >
Taste : <Dropdown style={styles.select} options={TASTE} defaultValue={LIKELIHOOD.find((t) => t.label === item.taste)} />
</div>
...
</div >
);
}
This isn't so much a hooks problem as a "where do I store my state" problem. So far I don't see any place in your implementation to store the users choices. Either the MenuItemDisplay component needs to maintain that state, or it needs to receive it from a parent component. Either way, that state (containing user choices) will need to be passed down (along with update functions) into the checkbox component as the value of a 'checked' prop, and the update functions for that state should be passed as (and adapted to) the checkbox 'onToggle' (or similar) prop

(react.js) why losing value of the fields and why is re-rendering?

what would be the reason I lose the value on the inputs when I try to add another one,?
in the component I set the useState with an array of an empty string, and when I click the add button it ads another slot in the array, but every time I add a field, the value in the rest of the form is reset and lost. I believe the app is re-rendering.
const IngridientsInputField = () => {
function newIngridient(event) {
event.preventDefault();
setStateIngridients([...stateIngridients, ''] )
setStateCounter(stateCounter +1)
}
return (
<div className="ingridients">
<h3>number of ingridients {stateCounter}</h3>
{ stateIngridients.map(() => (
<input type="text" name="ingridient" title="ingridient" placeholder={`ingridient (example: pineapple)`} />
))
}
<button onClick={newIngridient}>add + </button>
</div>
)
};

Using get and set on a data property object - VueJS

From what I know VueJS has a way to use a getter and setter for their computed properties per this documentation on Computed property.
I have here the vue component where you can see the amount is an object and we have a group of persons from the vuex store.
data() {
return {
form: {
amounts: {},
},
};
},
mounted() {
const persons = this.$store.getters.getPersons()
persons.forEach((person) => {
this.$set(this.form.amounts, person.id, '0.00');
});
},
I made it so I can associate a person to the amount he has paid on the form by linked it using the ID and the payment. This is an example of what this.form.amounts should look like.
{'HKYDUPOK': 0.00},
{'RYYJPUKA': 0.00},
{'KZMUYTAK': 0.00}
Now by default, their values should be 0.00, on the input number field where they entered the amount, by default I applied them to v-model which looks like this:
<div v-for="person in persons">
<input
class="form-control"
v-model="form.amounts[person.id]"
type="number"
step=".01"
min="0"
required>
</input>
</div>
But here is the thing, when you open your code snippet on the browser, you notice that the input number field has the default value of 0.00 which acts as somewhat a placeholder. I wanted to remove the default value of 0.00 on the number input and have it instead to an empty input yet the underlying value of the amounts per person is not null but still 0.00 or 0. This is so that the form is clear of input when the user tries to input values on the input box instead of having to erase and replace 0.00 with an actual value (Hope this is clear). Now there is a possibility that on the total amount, there are at least 1 or more persons with an amount of 0. I wanted to make sure that an empty input number field does not result in null but instead, it's 0. Is this possible?
I tried checking the computed property getter and setter for this to change the default binding yet how do you map the form.amounts to match the amount to its corresponding person? On the Get, if the value is not more than 0.00 or 0, then return an empty value to the input field. Set is the bigger problem for it only accepts one parameter which is called newValue and would be hard to say pass the personId to map the amounts to the corresponding person. Is there a way to touch upon and manipulate the binding of a data property which is an object yet also change the default behavior on the model to return empty instead of 0.00? I hope my question is clear enough.
I assume this is a follow on from your previous question...
At this stage, you're best creating a component to represent your data input element.
Something like this (using a single-file component example)
<!-- number-input.vue -->
<template>
<input class="form-control" type="number"
step=".01" min="0"
:value="amount"
#input="updated"
required />
</template>
<script>
export default {
name: 'NumberInput',
props: {
value: Number
},
computed: {
amount () {
return this.value || ''
}
},
methods: {
updated ($event) {
this.$emit('input', parseFloat($event.target.value) || 0)
}
}
}
</script>
Then you can use it in your parent template
<div v-for="person in persons" :key="person.id">
<NumberInput v-model="form.amounts[person.id]" />
</div>
Just remember to import and use the component...
<script>
import NumberInput from 'number-input'
export default {
components: { NumberInput },
// etc
}
</script>
JSFiddle Demo
Also see https://v2.vuejs.org/v2/guide/components.html#Using-v-model-on-Components

creating elements in React

I don't understand how elements are created in React.
I have some code below where the goal is to create elements on a form submit using a value from a refs - so for every submit in a form, it creates a new <h1> tag with the content of the textbox inside of it. A sample of what I'm trying to do looks like:
...
addHeader(e) {
e.preventDefault();
const newHeader = this.refs.post.value;
var newpost = React.createElement("h1", {
type: "text",
value: newHeader
});
}
...
render() {
return (
<div className="form-section">
{ newPost }
<form onSubmit={this.addHeader.bind(this)}>
<input id="input-post" type="text" placeholder="Post Here" ref="post" />
<input type="submit" value="Submit" />
</form>
<button className="form-section__submit" onClick={this.clearFields.bind(this)}>Clear All</button>
</div>
);
}
Basically my thinking is in my addHeader() function I'm assigning a variable of newPost to the method and calling it within my component. This code is causing 2 errors:
33:9 warning 'newpost' is assigned a value but never used no-unused-vars
49:13 error 'newPost' is not defined no-undef
What I don't understand, is (from what I can see) I am assigning a value to that variable and also using it in the component that I am rendering... along with that, I don't understand this error message. How can something be assigned a value but be undefined at the same time...? Is it because it's in the wrong scope? How do I declare where the new element is rendered specifically in the component?
I read the documentation but it doesn't give a clear answer as to how to control where in the component the new element is rendered.
Made some changes to your code. You're going to want to initialize component state in your constructor. In your addHeader method you will use this.setState to update the state of the component with a new posts value including the value of this.input. I changed your ref on the input an actual ref. You take the element and store on this. Every time you add a new post you will get a new <h1> with the value of the textarea.
...
addHeader(e) {
e.preventDefault();
this.setState((prevState, props) => {
return { posts: [ ...prevState.posts, this.input.value ] };
});
}
...
render() {
const { posts } = this.state;
return (
<div className="form-section">
{ posts.map( text => <h1>{ text }</h1> ) }
<form onSubmit={this.addHeader.bind(this)}>
<input id="input-post" type="text" placeholder="Post Here" ref={ el => this.input = ref } />
<input type="submit" value="Submit" />
</form>
<button className="form-section__submit" onClick={this.clearFields.bind(this)}>Clear All</button>
</div>
);
}
As an aside: Binding functions in the render method of react components will cause a performance hit. There is no need to re-bind the this context of the function on every render. this.clearFields.bind(this) should become this.clearFields and you will need to add this.clearFields = this.clearFields.bind(this) to your constructor. You do not need to bind functions that are not used as callbacks.
You're going to want to do the same thing for this.addHeader.bind(this).

translating between cents and dollars in html input in React

I'm in a bit of a weird situation, I am dealing with currency in my we app. On the model side, I am saving currency as cents before sending to the server as I don't want to deal with decimal points on the server side.
In the view however, I want the to display normal currency and not cents.
So, I have this input field where I take the data from dollars and change it to cents:
<input name="balance" type="number" step="0.01" min="0"
placeholder="Balance in cents" onChange={this.handleUpdate}
value={this.props.user.balance / 100} />
And when there's a change in the input value, I change it back to cents before sending it upstream:
handleUpdate: function(e) {
var value = e.target.value;
// changing it back from cents to dollars
value = parseFloat(value) * 100;
// save back to the parent component managing the prop
this.props.onUserUpdate(value);
}
This puts me in kind of a deadlock, there's no way for me to enter a decimal point "." Let me demonstrate :
33 in the input box --> becomes 3300 in the parent state --> goes back as 33 in component prop - all good
33.3 in the input box --> becomes 3330 in the parent state --> goes back as 33.3 in the component prop - all good
33. in the input box --> becomes 3300 in the parent state --> goes back as 33 in the component prop - this is the problem
As you can see in case #3, when the user first enters "." this doesn't translate back to the same number with "."
Since it's a controlled input, there's basically no way of writing "."
I have tried using uncontrolled element with defaultValue, but the amount prop is not ready the time the component is rendered so it's just empty
http://jsfiddle.net/fpbhu1hs/
Controlled inputs using derived values can be tricksy - if you need to be able to display invalid or otherwise weird input then...
always hold the input's value in its component's own state
<input value={this.state.value} onChange={this.handleUpdate} // rest as above...
derive the initial value in getInitialState()
getInitialState: function() {
return {value: this.props.user.balance / 100}
}
implement componentWillReceiveProps(nextProps) to detect when the prop's value is changing from above and re-derive the state value
componentWillReceiveProps: function(nextProps) {
if (this.props.user.balance != nextProps.user.balance) {
this.setState({value: nextProps.user.balance / 100})
}
}
Now when the user enters "33.", you store their literal input using setState(), then call back to the parent.
handleUpdate: function(e) {
var value = e.target.value
this.setState({value: value})
this.props.onUserUpdate(parseFloat(value) * 100)
}
If the value the parent then passes back down to the child via props hasn't changed (3300 == 3300 in this case), then componentWillReceiveProps() won't do anything.
Working snippet:
<script src="http://fb.me/react-with-addons-0.12.2.js"></script>
<script src="http://fb.me/JSXTransformer-0.12.2.js"></script>
<div id="example"></div>
<script type="text/jsx;harmony=true">void function() { 'use strict';
var Parent = React.createClass({
getInitialState() {
return {cents: 3300}
},
_changeValue() {
this.setState({cents: Math.round(Math.random() * 2000 + Math.random() * 2000)})
},
_onCentsChange(cents) {
this.setState({cents})
},
render() {
return <div>
<p><strong>Cents:</strong> {this.state.cents.toFixed(0)} <input type="button" onClick={this._changeValue} value="Change"/></p>
<Child cents={this.state.cents} onCentsChange={this._onCentsChange}/>
</div>
}
})
var Child = React.createClass({
getInitialState() {
return {dollars: this.props.cents / 100}
},
componentWillReceiveProps(nextProps) {
if (this.props.cents != nextProps.cents) {
this.setState({dollars: nextProps.cents / 100})
}
},
_onChange(e) {
var dollars = e.target.value
this.setState({dollars})
if (!isNaN(parseFloat(dollars)) && isFinite(dollars)) {
this.props.onCentsChange(parseFloat(dollars) * 100)
}
},
render() {
return <div>
<input type="number" step="0.01" min="0" value={this.state.dollars} onChange={this._onChange}/>
</div>
}
})
React.render(<Parent/>, document.querySelector('#example'))
}()</script>
I'm using this simple solution to handle controlled inputs and decimal values.
Create two props in your state, one to hold actual value and another to hold string.
constructor(props) {
....
this.state = {
myProperty: 1.42,
myPropertyString: '1.42'
}
}
Set your input value to String one
<input type="text"
onChange={this.handleUpdate}
value={this.state.myPropertyString}/>
In handleUpdate method update both state variables.
handleUpdate(e) {
this.setState({
myProperty: parseFloat(e.target.value),
myPropertyString: e.target.value
});
}

Categories

Resources