Programmatically assign a value prop to inputs through an object - javascript

I want to assign the onChange and value props (controlled input) by passing them in an object through destructuring.
The function getInputProps() returns the onChange and value props
getInputProps = name => {
return {
value: this.state.data[name],
onChange: this.handleInput
}
}
The input element consumes these props through destructuring
<input {...getInputProps('myInput')} />
However, that doesn't work, and I guess it's because the value is being passed as a 'value' and not a 'reference' to the state variable. It works when I put the value directly as a prop. What are my alternatives to pass all the necessary input props in an object?
EDIT: The handleInput function is working, when I omit the 'value' prop everything works as expected. It's the value that is problematic. When it is passed this way, it passes the actual value (which is none) to the HTML5 value parameter, and it is not dynamic anymore. When I omit 'value' in the object, then the user input is correctly set in the state (which is what the handleInput does), but changing the state doesn't change the value of the input.

Guys I found the solution. It is a simple stupid mistake. I was setting the name of the input and the 'name' passed to the function to different values. And the handleInput functions changes the state based on the actual name of the input.

Related

are event.target.value and value attribute of input element the same in react?

import React from 'react';
import { useState } from 'react';
function MyForm() {
[state, setState]=useState("");
return (
<form>
<label>Enter your name:
<input type="text"
value={state}
onChange={(event)=>setState(event.target.value)}
/>
</label>
</form>
)
}
When I do two-way binding in react I don't really understand how it works.
As I know, event.target is a property of an event object that refers to the element that triggered the event. So in this case, if we type something in input field, it will trigger the "onChange" which in turn sets the state to the value of element that triggered this change action (input element) and whilst its value is empty string(because its value attribute is equal to the state). So actually state setter function would have set the state to the current state that makes state and also value attribute always to be the same. But off course, I miss something here. Thanks in advance for any explanation.
If explain it step by step - it gonna be something like this (in the context of your example) :
State is the reactive variable, so then it changes, your component (and nested components) rerenders. On the first render you just set it to default.
Then, this variable is hardly assigned to the value of an input. So in a normal situation you wouldn't be able to change the input at all, BUT.
You set your listener to change the state then this input gets some users interaction. Your type something - your state changes (by that time input is still empty).
As we already know, we changed the state. Component rerenders with new state. For example - we typed letter "T".
This rerender causes new state to assignee to the value of your input, you get value="T".
And boom - you have your new letter.
It can be said that you don't really changing the input - state does.
Hope that explanation was helpful! ;)

Error writing to input e.target.value in array

There is an array of inputs in two forms: 1. Empty when created. 2. With the created values from the server after creation and saving. My code for adding a value for each of the inputs in the array doesn't work in the second case and I don't understand what could be wrong. In the first case, the values are written normally, and in the second, nothing happens.
<input
defaultValue={sectionValue[i].text}
value={sectionValue[i].text}
onChange={(e: React.ChangeEvent<HTMLInputElement>): void => {
sectionValue[i].text = e.target.value;
setSectionValue([...sectionValue]);
}}
/>
There are two issues:
Any time you're changing state based on existing state, you're best off using the callback form of your state setter, because it's really easy for the handler to close over a stale copy of the state (sectionValue in your case).
You're breaking one of the rules of state: don't directly modify state. You're directly modifying the sectionValue[i] object. So it's the same object, but with a different text property. Later you're copying the array, but you need to copy the object as well. Sometimes it'll happen to render correctly when you do this, but other times — often — it won't.
To fix both, change:
sectionValue[i].text = e.target.value;
setSectionValue([...sectionValue]);
to
setSectionValue(oldSectionValue => {
const newSectionValue = [...oldSectionValue];
newSectionValue[i] = {
...oldSectionValue[i],
text: e.target.value
};
return newSectionValue;
});
There's more than one way to do that, but the basic things are: 1. Use the callback, and 2. Copy both the object and the array.
Side note: Since sectionValue is an array, I'd suggest using the plural for it (sectionValues).

Angular 2 - pass an object field by reference. Reusable way to edit objects

I am creating reusable table component which will enable editing of objects fields to prepare them for sending to the API.
Having an object:
person: {
name: "John"
job: {
type: "IT"
title: "Software Engineer"
}
}
I would like to pass the object nested field to a component and edit. F.e:
<edit-field [field]="person.job.title"></edit-field>
Which results in an input field that edits exactly the title field in original object. The problem is that person.job.title is a string, not and object or array so it's not passed by reference.
I had 2 ideas haw the problem could be solved:
1) pass "person.job.title" as a string:
<edit-field [field]="'person.job.title'"></edit-field>
or
<edit-field field="person.job.title"></edit-field>
And in component class do split by a ".":
let fields = this.field.split('.');
and then do a while loop to access the field by reference.
We could also do 2 inputs:
<edit-field-component [fieldRef]="person.job" [field]="'title'"></edit-field-component>
and then inside component do this.fieldRef[this.field]
I am wondering if there is any other, more clean, way to achieve that.
Basically, you want to accomplish two-way binding - i.e. changes to the object value: eg person.job.title updates your new edit component, but also changes made from your edit component also get reflected back to the object value.
In Angular, that means you have to bind both ways:
<edit-field [field]="person.job.title" (change)="person.job.title=$event"></edit-field>
where your edit-field component has an #Output property that emits the changed value whenever someone types into it. The value emitted from the #Output property will be in the variable $event and you simply want to assign that back to the property that you want to update.
So, your EditFieldComponent can look something like:
#Component({
.....
template: `
<input (input)="change.emit($event.target.value)" .... />
`
})
export class EditFieldComponent {
change = new EventEmitter();
}
The above means that whenever an input event triggers on your input field, the component's change output property will emit the new value of the input field.
===========
If you understand everything above, then Angular provides a little shortcut for this exact scenario. If the output property of your component is named a specific way, you can simplify how you write the two way binding.
So, if your input property is field, and you name your output property fieldChange you can make use of some fancy syntax to cut down how much you have to type.
i.e.
<edit-field [field]="person.job.title" (fieldChange)="person.job.title=$event"></edit-field>
is equivalent to:
<edit-field [(field)]="person.job.title"></edit-field>
[field]="person.job.title" is one-way binding (changes of person.job.title are propagated to field but not the other way round)
[(field)]="person.job.title" would achieve two-way binding (changes made by fieldChange method are also propagated back to person.job.title)
If you want to reffer your object or property of your object to your component, you need to create an #Output property with type eventEmitter.
#Input('field') field: any;
#Output() fieldChange = new EventEmitter();
Be carefull to name your output property with "Change" suffix word. It will detect automatically change from your object and notify to your main object.
Javascript just like Java is passed by value, they have never offered passed by reference. So in your case, your best option is to pass your person object directly. Even though it will be copied inside your function, the copy still refers to the same object, so changing a field in the copy will also change the corresponding field in the original.

In Redux Form, how can I set the initial value of the checked property of a checkbox?

In our Redux Form 5.3 (not 6.x) app, I want to render an <input type="checkbox" /> like so:
// In some cases, fieldHelper.checked is initially undefined. Then, when the
// user clicks one of the checkboxes, fieldHelper.checked is
// explicitly set to true or false. This change from one of the component's
// props being undefined to having a value causes a React warning; see
// http://stackoverflow.com/a/38015169/473792 and
// https://facebook.github.io/react/docs/forms.html#controlled-components
// So to prevent that warning, we do a quick null check on
// fieldHelper.checked and replace it with false explicitly if it is
// undefined before rendering the checkbox.
const fieldHelper = this.props.field['checkbox'];
const checked = fieldHelper.checked || false;
const modifiedFieldHelper = Object.assign({}, fieldHelper);
delete modifiedFieldHelper.checked;
return (
<input
checked={checked}
{...modifiedFieldHelper}
/>
);
}
As noted in the comment, in my testing environment, this.props.field['checkbox'].checked is undefined immediately after mounting the <input>. As a result, when my tests modify the value of this.props.field['checkbox'].checked, I get the following warning:
Warning: ApplicantForm is changing an uncontrolled input of type checkbox to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
... Unless I explicitly set the checked prop on the <input> to false instead of undefined, as demonstrated in the code snippet I posted above.
Instead of using this null check, I would like to set the initial value of this.props.fields['checkbox'].checked before my tests run. I know I can set the initial value of fields in Redux Form. Is there a way to set the initial value of an auxiliary property like the checked property that Redux Form also controls?
You could create a simple conditional in your checked attribute so that when the value is undefined you just return false:
<input
type="checkbox"
checked={typeof this.props.fields['checkbox'].checked == 'undefined'?false:this.props.fields['checkbox'].checked}
onClick={() => {... your onClick code ...}} />
Checkboxes are no different from text inputs. You can still just destructure the field object into your <input>. See how the employed value is used in the v5.3.3 Simple Form Example.
<input type="checkbox" {...myField}/>
Redux Form will detect that it's a checkbox (really it detects for boolean values) and use the checked prop.
I've never used Redux-Form but to display the default checked value for a checkbox in default React you'll need to use the defaultChecked attribute. It accepts a boolean.
<input type="checkbox" defaultChecked={/*bool*/} />

ReactJS: How do I set the default value for input components?

How do I get around the anti-pattern with state from props when using default values for input fields?
When the form is rendered, it should contain some default values. When I set some values and closes the form and then re-opens the form I want the default values to show, not the ones previously entered...
But when I let state inherit values from props, the props are changed forever. I haven't found a way to get around this.
If you look at the default input component, or the inputs from react-bootstrap, or most of the DatePickers and Selects on the internet, you will see that most of them have a value prop, and most of them will have little to none state, at least when it comes to the selected value.
An input is meant to be uncontrolled, meaning that they are better off not having state, and just receiving the value as a prop, as well as a callback they should call when the value changes. It's up to the controller-view above them, to watch these events and change the model. Some times it may be convenient for the inputs to keep the value in the state as well, but they can only do it as long as they respect new value being passed as a prop, because the state persists in rerenders and getInitialState is only called once. See also: ReactJS: Why is passing the component initial state a prop an anti-pattern?
However, because it would be too cumbersome for the controller-view to handle all these value changes, React comes with an utility called ReactLink, which is, very briefly, a way to automatically bind an input to a property in the state of the controller-view. Example: You can bind an input to the property DateOfBirth of the model, which is part of the state of the controller-view.
Now back to your question, you don't actually pass the default value, you pass the value itself, as a prop. The controller-view should decide what it is. What is the value for the DateOfBirth field? None? Ok, I'll pass the default value as the value prop.

Categories