joy08
joy08

Reputation: 9662

How to push to an object to an array which is a state object, react way with Typescript

I need to maintain array of objects which is stored in a state object. Basically I need to push each object to this array whenever I click on Add button .This should basically store this object in array.

Also I am unable to fetch proper values when I am trying to submit? Where am I going wrong?

Basically the structure I want is:

users= [
        {"name":"xxx","email":"yyy","phone":"656"},
        {"name":"yyy","email":"xxx","phone":"55"}
       ];
import * as React from 'react';
interface IState{
    users : Account[];
}
interface Account{
  name: string;
  email: string;
  phone: string
}

export default class App extends React.Component<{},IState> {

    constructor(props:any){
       super(props);
       this.state= { 
                         users: []
                   }
    }


  handleChange = ( event: React.ChangeEvent<HTMLInputElement>) => {
   this.setState({
    users:{
      ...this.state.users,
      [event.target.name]:event.target.value
    }
    })
  }

  onAdd = () => {
     this.setState((prevState) => ({
     users: [...prevState.users],
    }));
    console.log(this.state.users); // Unable to get the proper info
  }

  render(){
   <React.Fragment>
     <form onSubmit={this.onAdd}>
       <input type="text" onChange={(e:any) => this.handleChange(e)} name={"name"} />
       <input type="text" onChange={(e:any) => this.handleChange(e)} name={"email"} />
       <input type="text" onChange={(e:any) => this.handleChange(e)} name={"phone"} />
       <button type="submit">Add</button>
      </form>
   </React.Fragment>
  }
}

Upvotes: 1

Views: 4049

Answers (4)

tarzen chugh
tarzen chugh

Reputation: 11257

Things to correct:-

1) You are using only one state variable users for one user as well as all users. So create two state variables, one for temporary storing of data for a user and users variable for storing all users data.

2) You are trying to access console.log(this.state.users); after setState but it is not in the callback, setState is asynchronous it should be in callback of setState.

3) When user submits the form, the page refreshes which is default behaviour of application, we need e.preventDefault(); to override this behaviour.

4) Use state for individual input textboxes so that you could may be apply validation etc on fields.

import * as React from "react";
import { render } from "react-dom";

interface IState {
  users : Account[],
  user: Account
}

interface Account{
  name: string;
  email: string;
  phone: string
}

class App extends React.Component<{}, IState> {
  constructor(props: any) {
    super(props);
    this.state = {
      users: [],
      user: {name: '', email:'', phone: ''}
    }
  }

  handleChange = (event: React.FormEvent<HTMLInputElement>) => {
    this.setState({
      user: {
        ...this.state.user,
        [event.currentTarget.name]: event.currentTarget.value
      }
    });
  };

  onAdd = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    this.setState(
      { 
        users: [...this.state.users, this.state.user],
        user: {name:'', email: '', phone: ''}
      },
      () => {
        console.log("updated state", this.state.users);
      }
    );
  };

  public render() {
    const { name, email, phone } = this.state.user;
    return (
      <form onSubmit={this.onAdd}>
        <input type="text" onChange={this.handleChange} value={name} name="name" />
        <input type="text" onChange={this.handleChange} value={email} name="email" />
        <input type="text" onChange={this.handleChange} value={phone} name="phone" />
        <button type="submit">Add</button>
      </form>
    );
  }
}

render(<App />, document.getElementById("root"));

Improvement Area - You could declare array of fields like fields = ['name', 'phone', 'email'] and map over in render function, this way you would need to write form once and any no of fields could be added.

Hope that helps!!!

Upvotes: 1

Roy.B
Roy.B

Reputation: 2106

you don't need handleChange, in onAdd get all inputs value from the event, put them in object( like {name: event.target.form.elements.name.value ...} and set it in users(which will be an array)

 export default class App extends React.Component{

    constructor(props:any){
       super(props);
       this.state = { 
             users: []
       }
    }

onAdd = (event) => {

 const user = {
   name: event.target.form.elements.name.value,
   email: event.target.form.elements.email.value,
   phone: event.target.form.elements.phone.value
  }
     this.setState({
     users: [...this.state.users, user]
    });

  }

  render(){
   <React.Fragment>
     <form onSubmit={this.onAdd}>
       <input type="text"  name="name" />
       <input type="text" name="email" />
       <input type="text" name="phone" />
       <button type="submit">Add</button>
      </form>
   </React.Fragment>
  }
}

then if you log this.state.users you will get the stractue you need

users= [
        {"name":"xxx","email":"yyy","phone":"656"},
        {"name":"yyy","email":"xxx","phone":"55"}
       ];

Upvotes: 1

KT-mongo
KT-mongo

Reputation: 2212

export default class App extends React.Component<{},IState> {

state = {
      users: [],
      text: '',
}


  handleChange = ( event: React.ChangeEvent<HTMLInputElement>) => {
   this.setState({
    [e.target.name]: e.target.value
    })
  }

  onAdd = () => this.setState({
    users:[
          ...this.state.users,
          {
           e.target.name: e.target.value,
           e.target.email: e.target.value,
           e.target.phone: e.target.value
          }
    ]
  })

  render(){
   <React.Fragment>
     <form onSubmit={this.onAdd}>
       <input type="text" onChange={(e:any) => this.handleChange(e)} name={"name"} />
       <input type="text" onChange={(e:any) => this.handleChange(e)} name={"email"} />
       <input type="text" onChange={(e:any) => this.handleChange(e)} name={"phone"} />
       <button type="submit">Add</button>
      </form>
   </React.Fragment>
  }
}

You are setting your users as an array and then you are converting it into an object with the handleChange. Try to see if this works

Upvotes: -1

Shridhar Sharma
Shridhar Sharma

Reputation: 2385

Update onAdd like below, because setState method updates state asynchronously, therefore you can't get the state right after calling it, but you can do so using a callback as second argument in setState method which is invoked after the method has updated the state

onAdd = () => {
  this.setState((prevState) => {
    const newUser = {}
    return {
      users: [...prevState.users, newUser],
    }
  }, () => {
    console.log(this.state.users)
  });
}

Upvotes: 2

Related Questions