45

I've a series of user data elements which I'm collecting inside a React component using hooks.

const [mobile, setMobile] = useState('');
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');

Each of these are updated as follows.

<input type="text"
       className="form-control"
       id="mobile"
       placeholder="Enter a valid mobile number"
       onChange={event => {setMobile(event.target.value)}}/>

Is there a more succint way to do this using an object as the variable?

3
  • 1
    Sure, you could... use an object as the variable. const [values, setValues] = useState({ mobile: "", ... });. Commented Jan 19, 2020 at 20:05
  • @jonrsharpe how do I call the set methods, inside the individual onChange events. Commented Jan 19, 2020 at 20:07
  • 2
    setValues({ ...values, [prop]: newValue });? Commented Jan 19, 2020 at 20:09

6 Answers 6

92

You should add name attributes to input tags. Each name must refer to key in AllValues object.

const [allValues, setAllValues] = useState({
   mobile: '',
   username: '',
   email: '',
   password: '',
   confirmPassword: ''
});
const changeHandler = e => {
   setAllValues({...allValues, [e.target.name]: e.target.value})
}
return (
   <input type="text"
       className="form-control"
       id="mobile"
       name="mobile"
       placeholder="Enter a valid mobile number"
       onChange={changeHandler}
   />
   // ...
)
Sign up to request clarification or add additional context in comments.

5 Comments

I did not realize why [e.target.name] has to be inside []?
This is the way to add object key by variable (e.target.name) name.
why should we use the name instead of the id? Convention only?
because ID has to be unique, but multiple elements can have same names.
TS7006: Parameter 'e' implicitly has an 'any' type.
26

The above answer could create issues in some cases, the following should be the right approach.

const [allValues, setAllValues] = useState({
   mobile: '',
   username: '',
   email: '',
   password: '',
   confirmPassword: ''
});
const changeHandler = e => {
   setAllValues( prevValues => {
   return { ...prevValues,[e.target.name]: e.target.value}
}
}

2 Comments

This should be safe more than the accepted answer. I have experienced some tricky bugs and this answer solved that problem.
For React 16 and earlier, this might cause a problem because of Event Pooling. Be sure to use this only for React 17 or later. Note that you can still use this answer with some additional lines like <_name=e.target.name;_value=e.target.value> and using _name and _value instead of using e directly in the return phrase.
7

Set initial values

const initialValues = {                   // type all the fields you need
name: '',
email: '',
password: ''
};

UseState

const [values, setValues] = useState(initialValues);       // set initial state

Handling the changes

const handleChange = (e) => {                
  setValues({
    ...values,                                // spreading the unchanged values
    [e.target.name]: e.target.value,          // changing the state of *changed value*
  });
};

Making the Form

<form>
  <input type="email"
   id="xyz"
   name="email"                                     // using name, value, onChange for applying changes
   value={email}
   onChange={changeHandler}
   placeholder="Enter your email"
   />
   // other inputs
</form>

Comments

0

to store multiple state in usestate for radio button in antd

 const [allValues, setAllValues ]= useState({
        validate:'',
        required:'',
        labelAlign:'',
        labelFontWeight:''
    })
    const changeHandler = (e, name) => {             
        setAllValues({
          ...allValues,                                
        [name]: e.target.value,          
        });
      };
     <Form.Item
            label="Validation"
            rules={[
              {
                required: true,
              },
            ]}
          >
          <Radio.Group
            onChange={(e) => changeHandler(e, "validate")}
            value={allValues.validate}
            style={{ paddingLeft: "15%" }}
          >
            <Radio value="True">True</Radio>
            <Radio value="False">False</Radio>
          </Radio.Group>
          </Form.Item>

Comments

0

You may refer this (usestate with single object):

const [formData, setFormData] = useState({
    mobile: '',
    username: '',
    email: '',
    password: '',
    confirmPassword: ''
  });

  const handleChange = (event) => {
    // Update the state object with the new value
    setFormData({...formData, [event.target.name]: event.target.value });
  };

Comments

0

I have run into the same situation many times and have written use-state-multiple package. It allows working with multi-value component state using one useStateMultiple() hook call and one setter function.

For documentation and installation see the repo: https://github.com/aptivator/use-state-multiple.

import {useStateMultiple} from 'use-state-multiple';

export function Component() {
  let [state, patchState] = useStateMultiple();
  let {phone = '', username = '', email = ''} = state;

  /*
    ...
  */

  patchState('username', 'some-user-name');
}

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.