React Dynamic Form with React State - reactjs

So i need to create a form in react with dynamics fields , these fields are elements in an Array ,
so i have to create an Object (which is a state using useState hook) from the Form , and the fields should be the properties of this object , my code :
const [object, setObject] = useState({})
const onChange = e => {
setObject({...object,[e.target.name]:e.target.value})
}
const properties = ["to","placeFrom","placeTo","from","date"]
{properties.length !== 0 &&
<form>
{properties.map(property =>
<input type="text" class="form-control" name={property} value={object.property} onChange={onChange}/>
)}
</form>
}
The Object in state should be like this :
{
to: value,
placeFrom: value,
placeTo: value,
from: value,
date: value,
}
the problem is in the value attribute in the input , i couldn't make it dynamic too.
Thanks in advance!

The problem is how you are accessing the object state when setting the value for the <input/>.
By doing object.property, you are asking javascript to provide the value associated with the property key on the object. That would return a value if your state object was of the form:
{
property: 'some-value'
}
In this case there is no key in your state called property and this is not the intended behavour.
To access the object dynamically with the variable property, you can do this: object[property].
Thus your input would look like
<input type="text" class="form-control" name={property} value={object[property]} onChange={onChange}/>
I might also suggestion that instead of calling your state object, you could provide a more descriptive name like formValueState or something like that.
Good luck!

Related

Access string of selected place in StandaloneSearchBox in react-google-maps/api

I'm wondering how I can access the actual value in a <StandaloneSearchBox ..> component after the user selected it from the autocomplete dropdown menu. I basically want to make it a controlled component.
I tried getPlaces() but that does not give me the actual value visible on the page but rather a bunch of structured data.
Take the example from the official docs (https://react-google-maps-api-docs.netlify.app/#standalonesearchbox), I basically want to do something like:
...
const [inputFieldValue, setInputFieldValue] = useState ("")
const onPlacesChanged = () => {
...
const value = ... // 1. access value
setInputFieldValue (value) // 2. store in state
}
...
<input
type="text"
placeholder="Customized your placeholder"
style={{...}}
value= {inputFieldValue}
/>
...

Event.target types for name input in FromHandler with TypeScript

I have a simple form in a React SPA with typescript.
I'm going to make this form processing simple, without adding component state and so, just to get value from the named input, name = 'gameId'.
There are two ways I've tried in the example below:
If I use event.target, TS says the "children" doesn't exist on type 'EventTarget'
If I use event.currentTarget, TS says the "gameId" doesn't exist on type 'HTMLCollection'.
Is there any way to solve these?
const submitAcceptGameHandler: React.FormEventHandler<HTMLFormElement> = async (event) => {
event.preventDefault();
// 1st
// Property 'children' does not exist on type 'EventTarget'.ts(2339)
const inputData = event.target.children.gameId.value;
// 2nd
// Property 'gameId' does not exist on type 'HTMLCollection'.ts(2339)
const inputData = event.currentTarget.children.gameId.value;
console.log(inputData);
};
<form onSubmit={submitAcceptGameHandler}>
<button type="submit">ACCEPT GAME</button>
<input name="gameId" type="text" placeholder={'Input game id...'}></input>
</form>
The issue with the event.target.children
children is an HTMLCollection, similar to an array. You can get values by its index children[0] or chilren[1]
Either you need to go over all your children or convert it to an array and find the desired component by its name or id
To convert you can just use the spread operator
const children = [...event.target.children]
const gameId = children.find(child => child.name === "gameId")?.value
ANYWAY
I would highly recommend having control over your fields and always having state management for them.
You can even use some libraries to manage your forms.
I prefer using react-hook-form, but you can choose anything you want

Adding new property to state and setting state and then destructing the state object i am unable to get the property added

I have added a property to state.
const[person, setPersonObj] = useState({});
setPersonObj({...person, age: 24});
and using in jsx and i can see the age.
<input type="text" value = {person.age} />
but when I am again destructing person object it is not showing me the age property.
const personObj = {...person};
personObj.age is undefined;
All this is done in one event.
I notice that you are using useState hook like this right?
const [personObj, setPersonObj] = useState(...);
So the problem here is when you use setPersonObj, the state personObj will update with new property age, not the variable person. So when again you set personObj as person, which not contain age, it will not contain age property.

Checkbox/Radio onChange event not returning the standard event object (Only if React-plus-Carbon)

I'm just starting with React, combined with the Carbon Design System from IBM (https://www.carbondesignsystem.com/). My problem is that Checkbox and RadioButton components do not return the same {event} object to my onChange handler as that returned by straight React/JSX. Carbon DOES match React on TextInput and Select components.
The behavior is similar to the issue in React onChange event doesnt return object. However, that is referencing React Toolbox and Material Design, which I am not using.
The difference can be seen in Carbon's online sandbox tool. An example of a radio button is at http://react.carbondesignsystem.com/?selectedKind=RadioButton&selectedStory=Default&full=0&addons=1&stories=1&panelRight=0&addonPanel=storybook%2Factions%2Factions-panel. Click the button and see the results returned in the Action Logger panel. Compare to results of changing (not just clicking) a text input in http://react.carbondesignsystem.com/?selectedKind=TextInput&selectedStory=Default&full=0&addons=1&stories=1&panelRight=0&addonPanel=storybook%2Factions%2Factions-panel. The problem element returns {event} as the third element down, while the good one returns only one element containing the {event} as desired. Apparently, Carbon is only passing along the first element.
Below is the change handler, the ReactJSX, and the resulting HTML.
handleChange(event) {
console.log(event)
const {name, value, type, checked} = event.target
this.setState({[name]: value})
}
<RadioButton
id="gender-male"
name="gender"
labelText="Male (Carbon)"
labelPosition="right"
value="male"
checked={this.state.gender === "male"}
onChange={this.handleChange}
/>
<div class="bx--radio-button-wrapper">
<input
id="gender-male"
name="gender"
type="radio"
class="bx--radio-button"
value="male"
>
<label for="gender-male"
class="bx--radio-button__label"
aria-label="Male (Carbon)">
<span class="bx--radio-button__appearance"></span>
<span class="">Male (Carbon)</span>
</label>
</div>
The expected behavior is for Local Scope to contain an event object with properties for name, value, type, checked, etc. But for Carbon's RadioButton and Checkbox, Local Scope contains only a flat text of the element's value, no object at all.
Global Scope does seem to contain the full object. But I hesitate to reference outside of Local, and it seems wrong to have to.
The following code is straight from the RadioButton component of the carbon-components-react library.
handleChange = evt => {
this.props.onChange(this.props.value, this.props.name, evt);
};
source
The code shows that the event is passed as the third argument, following the contents of the value and name props of the RadioButton component.
So you should be able to handle changes like this:
handleChange(value, name, event) {
console.log(event)
const {type, checked} = event.target // Should not be needed anymore.
this.setState({[name]: value})
}

as am not getting what is the functionality of below code

Can someone explain me below function line by line
handleChange(e) {
let fields = this.state.fields;
fields[e.target.name] = e.target.value;
this.setState({
fields
});
as my understanding
we are creating a variable fields and storing the present state of fields in the variable because we cant mute the state.
i don't have idea about fields[e.target.name] = e.target.value;
updating the fields
can someone explain me the above function line by line?
fields[e.target.name] this syntax is also used to access the Object Properties.
var obj = {name:'abc', color:'red'}
console.log(obj.name) // this print 'abc'
console.log(obj['name']) // also print 'abc'
with obj['xxx'] syntax you can dynamically read and set properties to the object.
reference https://www.w3schools.com/js/js_objects.asp
So in your problem fields variable take existing fields object from the local state. And in each event it create new property with that event target name and add that event target value as that new property value. if that property name already exist within the fields object then it only modified the value of that property.
So as in your list,
is correct. With let fields = this.state.fields; it get previously stored values into the fields variable. without losing previous data
this is what I explained above
your answer is correct. update the state with old and new values
This code let's you dynamically change the value of a field in your component state by using object notation. object[]
By using fields[event.target.name] you're going to look for a field in your component state that matches the name of the element that's causing the event to occur. Then it looks like you're updating the value for that field with event.target.value
Why this is useful
Let's say you have a component where you want to retrieve multiple inputs from a user. Without object notation, you might end up writing very repetitive code like writing a different event handler for each input in order to determine what field to update in your component state:
BAD:
handleOnChange1 = (event) => (this.setState({userName: event.target.value}))
handleOnChange2 = (event) => (this.setState({lastName: event.target.value}))
However, by naming your elements and coordinating them with a matching field in your component state, you won't have to worry about writing additional event handlers as long as you use object notaton.
GOOD:
class UserForm extends React.Component{
state = {
firstName: "",
lastName: ""
}
handleOnChange = (event) => {
this.setState({
[event.target.name]: event.target.value
})
}
render(){
return(
<div>
<input name="firstName" value={this.state.firstName} onChange={this.handleOnChange}/>
<input name="lastName" value={this.state.lastName} onChange={this.handleOnChange}/>
</div>
)
}
}

Resources