0

I am trying to create a form dynamically based on array of objects.

Goal here is to dynamically create user accounts on click of Add User button and submit the entire state object of users to the backend.

Getting this error 'Element implicitly has an 'any' type because type '{ name: string; age: string; }' has no index signature'.

I am new to react ! Help would be appreciated.

What am I doing wrong?

import * as React from "react";

interface State{
  users:User[]
}
interface Props{

}
interface User{
  name:string,
  age:number;
}

class App extends React.Component<Props,State> {

   state = {
      users: [{name:"", age:""}],
   }

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

   addUser = () => {    
    this.setState((prevState) => ({
      users: [...prevState.users, {name:"", age:""}],
    }));
   }

   handleSubmit = (e:any) => { 
       e.preventDefault();
       console.log(this.state.users) 
   }

   render() {
     let { users} = this.state
     return (
      <form onSubmit={this.handleSubmit} >
        <button onClick={this.addUser}>Add new cat</button>
        {
          users.map((val, id)=> {
            return (
              <div key={id}>
                <input
                  type="text"
                  name="name"                 
                  value={users[id].name}                   
                  onChange = {(e) =>this.handleChange(id,e)}
                />

                <input
                  type="text"
                  name="age"
                  value={users[id].age}                  
                  onChange = {(e) => this.handleChange(id,e)}
                />
              </div>
            )
          })
        }
        <input type="submit" value="Submit" /> 
      </form>
    )
  }
}
export default App;

2 Answers 2

2

The type of event.target.value is string and not necessarily 'name' | 'age' so when you are calling:

users[value][event.target.name] = event.target.value;

Typescript cannot be certain that users[value] will have this index. That is why you can either change the User interface:

interface User {
    name:string,
    age:number;
    [key: string]: string | number
}

or you can check that the key is what you are expecting it to be:

const keyName = event.target.name
if (keyName === 'name' || keyName === 'age') {
    users[value][keyName] = event.target.value;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Perfect! Thanks much
1

First the interface for User is wrong. age is cast to a number, and you set age to an empty string "".

Secondly when you use Array.prototype.map you don't use the index [id]. The implementation of you map() function looks a bit weird. From MDN

The map() method creates a new array with the results of calling a provided function on every element in the calling array.

You can read more about map here

import * as React from "react";

interface Props{

}

interface User {
  name: string;
  age: number | string;
}

interface State {
  users: User[];
}

class App extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      users: [{ name: "", age: "" }]
    };
  }

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

  addUser = () => {
    this.setState(prevState => ({
      users: [...prevState.users, { name: "", age: "" }]
    }));
  };

  handleSubmit = (e: any) => {
    e.preventDefault();
    console.log(this.state.users);
  };

  render() {
    const { users } = this.state;
    return (
      <form onSubmit={this.handleSubmit}>
        <button onClick={this.addUser}>Add new cat</button>
        {users.map((user, id) => {
          return (
            <div key={id}>
              <input
                type="text"
                name="name"
                value={user.name}
                onChange={e => this.handleChange(id, e)}
              />

              <input
                type="text"
                name="age"
                value={user.age}
                onChange={e => this.handleChange(id, e)}
              />
            </div>
          );
        })}
        <input type="submit" value="Submit" />
      </form>
    );
  }
}
export default App;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.