React set state with variable that is nested - reactjs

I have an input that looks like
<input name="data.company.name" value="Bob's Burgers" />
and on it's onChange I'd like to update the state with it's name.
<input name="data.company.name" value="Bob's Burgers" onChange={(e) => this.setState({ [e.name]: e.target.value}) />
But when I do this the state looks like
this.state = {
data: { company: { name: '' } },
data.company.name: "Bob's Burgers"
}
How can I achieve passing the name to the state so I end up with
this.state = {
data: { company: { name: "Bob's Burgers" } }
}
I cannot alter the name of the input as it's set via a Field Component.

If you don't mind adding lodash to your project you could do this:
<input
name="data.company.name"
value="Bob's Burgers"
onChange={e =>
e.persist();
this.setState(prevState => {
return _.setWith(_.clone(prevState), e.name, e.target.value, _.clone);
});
}
/>;

Related

Updating a form object with React useReducer

I have a controlled form containing an address object.
const [formData, setFormData] = useReducer(formReducer, {
name: "",
address: { addressLine1: "", city: "", state: "", zip: "" },
phone: "",
contact: "",
});
const formReducer = (state, event) => {
return {
...state,
[event.name]: event.value,
};
};
Updating a form input triggers a handler function.
<input
className={style.bodylessInput}
placeholder=" "
type="text"
maxLength={30}
name="name"
value={formData.name}
onChange={handleChange}
/>
function handleChange(event) {
setFormData({
name: event.target.name,
value: event.target.value,
});
}
I am struggling to update the address fields. Using an input with name="addressLine1" will add a property to the form object rather than update the address.
Any advice on how I can update the address object inside my form object would be appreciated.
You can do as below. input name be like address.addressLine1
function formReducer(state, event) {
if(event.name.startsWith("address") && event.name.split(".").length > 1){
var updateField = event.name.split(".")[1];
return{
...state,
address:
{...state.address, [updateField] : event.value}
}
}
return {
...state,
[event.name]: event.value,
};
}
input:
<input
type="text"
maxLength={30}
name="address.addressLine1"
value={formData.address.addressLine1}
onChange={handleChange}
/>

React TypeScript: Set initial value for input with useRef

The input field displays the value saved in local storage, but I can't edit the value in the input field and I don't know why.
I don't want to use a placeholder as I want to be able to edit the values.
import React, { useRef, useState } from 'react';
const ProfileComponent: React.FC = () => {
let email = useRef<HTMLInputElement>(null);
const saveEmail = () => {
localStorage.setItem('email', email)
}
// tslint:disable-next-line: no-any
const update = (event: any) => {
if (event.target.name === 'email') {
setState({ ...state, email: event.target.value });
} else if (event.target.name === 'fullName') {
setState({ ...state, fullName: event.target.value });
}
};
interface StateInterface {
email: string;
}
const [state, setState] = useState<StateInterface>({
email: localStorage.getItem('email') || '',
});
return (
<input type='text' name='fullName' ref={fullName} onChange={update} value={state.fullName} />
<input type='text' name='email' ref={email} onChange={update} value={state.email} />
<button onClick={saveEmail}></button>
)
}
There are a few issues with the code you have provided
1) You should wrap the DOM elements with React Fragments (<> </>)
2) Instead of setting the type of event as any, you might want to use React.FormEvent<HTMLInputElement>.
3) You should use localStorage.setItem('email', state.email) instead of localStorage.setItem('email', email), since email is a property as part of the state object, thus you will have to reference it in order to access the values.
Here are the full changes below:
interface StateInterface {
email: string;
fullName: string;
}
const ProfileComponent: React.FC = () => {
let email = useRef<HTMLInputElement>(null);
let fullName = useRef<HTMLInputElement>(null);
const [state, setState] = useState<StateInterface>({
email: 'aa#gmail.com' || '',
fullName: 'aa' || '',
});
const saveEmail = () => {
localStorage.setItem('email', state.email)
console.log(state);
}
const update = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.name === 'email') {
setState({ ...state, email: event.target.value });
} else if (event.target.name === 'fullName') {
setState({ ...state, fullName: event.target.value });
}
};
return <>
<input type='text' name='fullName' ref={fullName} onChange={update} value={state.fullName} />
<input type='text' name='email' ref={email} onChange={update} value={state.email} />
<button onClick={saveEmail}>save</button>
</>
}
You have to have an onChange in your input
return (
<input type='text' name='email' ref={email} onChange={e => setState({email: e.target.value})}
value= {state.email} />
<button onClick={saveEmail}></button>
)

Load data into inputs when entering the code

I have updated the Code.
Here I have a functional Select Autocomple showing the list of records from DB "Register". When selecting a Code, the Name value is automatically renamed.
The same thing I want to do but with the not with , I want to call more than two values like this in the image and in select is only Label and Value
Capture: [1]: https://i.stack.imgur.com/ELf1a.png
class Register extends Component {
state = {
status: "initial",
data: [],
name:'',
code:''
}
componentDidMount = () => {
this. getInfo()
}
getInfo= async () => {
try {
const response = await getAll('register')
console.log(response.data)
this.setState({
status: "done",
data: response.data
});
} catch (error) {
this.setState({
status: "error"
});
}
};
handleChange = (selectedOption) => {
this.setState({
selectedOption,
name: selectedOption.value
});
render() {
//show Name and code on Select from Register
const data = this.state.data.map( st => ({value: st.Name, label: st.Code}));
return (
<Container>
<RowContainer margin="1px" >
<ColumnContainer margin="10px">
<h3>Info</h3>
<label>Code</label>
<Select
width='215px'
value={selectedOption}
onChange={this.handleChange}
options={data}
name={"Code"}
/>
<label>Name</label>
<Input
width='150px'
type="text"
name={"Name"}
placeholder="Name"
value={this.state.name} />
</ColumnContainer>
</RowContainer>
</Container>
)
}
};
export default Register;
You want to know how change the state for <input/>
try this
constructor(props){
super(props)
this.state = {
status: "initial",
data: [],
codigo: "",
nombre: ""
}
}
handleChange(event){
let stateUpdate = this.state;
stateUpdate[event.target.name] = event.target.value}
this.setState(stateUpdate);
}
render() {
const data = [...this.state.data];
return (
<Container>
<RowContainer margin="1px" >
<ColumnContainer margin="10px">
<h3>Info</h3>
<label>Codigo</label>
<Input
name="codigo"
width='150px'
type="text"
placeholder="Digite el codigo"
value={data.codigo } ref="codigo" />
<label>Nombre</label>
<Input
name="nombre"
width='150px'
type="text"
placeholder="Nombre completo"
value={this.state.nombre} />
</ColumnContainer>
</RowContainer>
</Container>
)
}

Getting error on React Component on Render

As I an New to ReactJS.
What i am doing is when i type is any field State should be update in particular field -
As This is my LoginComponet and Setting small Form -
import React, { Component } from 'react';
import '../css/style.css';
export class LoginCompoent extends Component {
constructor(props) {
super(props);
this.state = {
field: {
phone: {
value: '',
validations: [],
errors: []
},
password: {
value: '',
validations: [],
errors: []
}
}
};
this.handelChangeEvent = this.handelChangeEvent.bind(this);
}
componentDidMount() {
}
handelChangeEvent(event) {
this.setState({
field: {
[event.target.id]: {
'value': event.target.value
}
}
});
}
render() {
console.log(this.state);
return (
<div className="loginMainDiv" >
<div className="">
<input className="login_inp" placeholder="Mobile Number"
value={this.state.field.phone.value}
onChange={this.handelChangeEvent}
type="text" name="phone" id="phone"
/>
<input className="login_inp" placeholder="Password"
value={this.state.field.password.value}
onChange={this.handelChangeEvent}
type="password" name="password" id="password"
/>
<button className="login_btn" >Login Safely</button>
</div>
</div>
);
}
}
Expected Result - on console.log(this.state);
when I type 9 in phone field -
field: {
phone: {
value: '9',
validations: [],
errors: []
},
password: {
value: '',
validations: [],
errors: []
}
}
Getting Result -
field: {
phone: {
value: '9'
}
}
I don't know why all fields are suddenly hidden when i update only phone field. ?
Because of this password is not setting in the form. ERROR - this.state.field.password is undefined ???
Deep merging issues, as you set a name property to your input, this should work:
this.setState(prevState => ({
field: {
...prevState.field,
[event.target.name]: {
'value': event.target.value
}
}
}));
In your handleChangeEvent function, you are updating the value of the field in the state:
this.setState({field: {
[event.target.id]: {
'value': event.target.value
}
}})
This will obviously overwrite the existing value of the field.
In your case, I would recommend using the callback function inside the setState. Please see the docs.
For example, if you want to update the value of the phone but also you want the value of the password to remain unchanged, You could do something like this:
handleChangeEvent = (event) => {
this.setState((prevState) => {
return {field: {
[event.target.id]: event.target.value,
...prevState.field
}
};
});
}
As I have tried all the Above answers but facing the error.
ERROR- Uncaught TypeError: Cannot read property 'id' of null on this.setState( prevState => ( {} ) ).
The Problem was - the reference of event are not maintain in async call that's why event.target is becoming null and getting above Error.
I got the concept of event.persist() which helps to maintain all the references of the event and make the Wrapper to the browser event with async calls.
You can go to this Article Reference

wants to add new objects in table in react js

I have a table, it's first three rows are hardcoded. After first three rows I want to add new objects in table. I write code for it but when I enter new data my old data is erase from table and new data is appear. I want it at its place and wants to add new data exactly after it. Here is the code of my state
constructor(props)
{
super(props)
{
this.state={
id:'',
name:'',
birth:'',
data:[
{
id:'1',
name:'Muhammad Ali jinnah',
dateofBirth:'1876'
},
{
id:'2',
name:'Allama Iqbal',
dateofBirth:'1877'
},
{
id:'3',
name:'Ahmad Bilal',
dateofBirth:'1992'
}
],
}
}
in that state i have array of objects i have hardcoded and state for data which i used to get data from my input box..input box are used to get data from user and add data in table and submit used to add data in table by using function..
here is code for my handle submit where i want to setstate for new object
handleSubmit(event) {
console.log('A ID:name and birth was submitted: ' + this.state.id,this.state.name,this.state.birth);
const { id, name, birth } = this.state;
const newdata = {
id: id,
name: name,
dateofBirth: birth
};
this.setState(prevState => ({
data: [prevState.data,newdata ]
}));
console.log("new array",this.state.data)
event.preventDefault();
}
I want to change its state but i also want my first three rows as i hardcoded
The error is occurring because you are not using the spread operator for inserting into the array.
When you use something like:
this.setState(prevState => ({
data: [prevState.data,newdata ] //This is wrong
}));
prevState.data becomes the first element of new array and new data becomes the second, instead you can use the spread operator for new array like:
this.setState(prevState => ({
data: [...prevState.data,newdata ]
}));
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
id: "",
name: "",
birth: "",
data: [
{
id: "1",
name: "Muhammad Ali jinnah",
dateofBirth: "1876"
},
{
id: "2",
name: "Allama Iqbal",
dateofBirth: "1877"
},
{
id: "3",
name: "Ahmad Bilal",
dateofBirth: "1992"
}
]
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
console.log('A ID:name and birth was submitted: ' + this.state.id,this.state.name,this.state.birth);
const { id, name, birth } = this.state;
const newdata = {
id: id,
name: name,
dateofBirth: birth
};
this.setState(prevState => ({
data: [...prevState.data,newdata ]
}));
console.log("new array",this.state.data)
event.preventDefault();
}
render() {
return (
<main>
<form onSubmit={this.handleSubmit}>
<input name='id' type='number' value={this.state.id} onChange={this.handleInputChange} placeholder='ID' />
<input name='name' value={this.state.name} onChange={this.handleInputChange} placeholder='Name' />
<input name='birth' type='date' value={this.state.birth} onChange={this.handleInputChange} placeholder='Date of Birth' />
<button type='submit'>Add New</button>
</form>
<table className='content'>
<tbody>
{
this.state.data.map(item=>{
return (
<tr>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.dateofBirth}</td>
</tr>
);
})
}
</tbody>
</table>
</main>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Following is the pseudo code that should solve your issue.
const prevData = this.state.data;
// Make first 3 are not overridden
var preserved = prevData.splice(3)
preserved.push(...newData)
this.setState({ data : preserved});
If I'm understanding correctly... You should be able to create a render with 3 static rows, then dynamically append rows based on the object you have in state (this.state.data). See below for example.
render {
return (
<table>
<tr><td>sample1</td></tr>
<tr><td>sample2</td></tr>
<tr><td>sample3</td></tr>
{
this.state.data.map((dataElement) => {
<tr><td>{dataElement.name}</td></tr>
});
}
</table>
)
}
let temp=this.state.data;
temp=temp.push(newdata)
Add these lines into your handleSubmit() and set temp into your state by using setState method.

Resources