score:20

Accepted answer

You should use setState with callback function:

setState(prev => ({ 
    ...prev,
    email: 'new mail',
}))

You'll create a new state object, which was created by previous state. And you can override anything you need. You'd need more new objects if you'd have a complex state object.

score:0

Try

this.state = { email : "", password : "" }

const handleChange = e => {
    const updated = {name , value} = e.target
    const next = {...this.state, ...updated}
    this.setState({next})
}

score:0

Try this:

  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')

    const handleChange = (name, e) => {
      switch (name) {
      case 'email':
        setEmail(e);
        break;
      case 'password':
        setPassword(e);
         break;
       default:
       break;
    }
  };

Your inputs:

<input
    type="text"
    onChange={ e => handleChange('email',e)}
    name="email"
  />
<input
    type="text"
    onChange={ e => handleChange('password',e)}
    name="password"
  />

score:0

CREATE A DICTIONARY OF ALL INPUTS

You could save a reference of the state and setState for each input in an object (serving as a dictionary), using the name of the input.

Below example also works for the checkbox inputs:

import React, { useState } from "react";
const setStateOfInput = 1;

function Form() {
  const inputs = {}; // Dictionary used to save a reference [state, setState] for each input (e.g. inputs.todo)
  const [todoInput, setTodoInput] = inputs.todo = useState("");
  const [doneInput, setDoneInput] = inputs.done = useState(false);

  function handleInput(e) {
    let { name, value, type } = e.target;
    // If input is a checkbox
    if (type === "checkbox") value = e.target.checked;

    inputs[name][setStateOfInput](value);
  }

  return (
    <div>
      <form>
        <label>To Do</label>
        <input name="todo" value={todoInput} onChange={handleInput} />

        <label>Done</label>
        <input name="done" type="checkbox" checked={doneInput} onChange={handleInput}/>

        <button type="button">Add</button>
      </form>
    </div>
  );
}

export default Form;

Edit react-hooks-controlled-component-form

CREATE A CUSTOM HOOK

In addition, the above could be refactored to create a custom hook that can be used for forms/inputs in other components:

function useInputState(initialValue, name, inputsDict) {
  if (inputsDict[name]) throw new Error(`input "${name}" already exists.`);

  inputsDict[name] = useState(initialValue);
  return inputsDict[name]
}

|

const inputs = {}; // Dictionary used by `useInputState`
const [todoInput, setTodoInput] = useInputState("", "todo", inputs);

Edit react-hooks-controlled-component-form

score:1

// The useState Hook is used in React,
const {obj, setObj} = useState({ // Initially a value of `{}` object type
  foo: {},
  bar: {}
})

const handle = event => {
  setObj({   // here `{}` is a new value of type object
    ...obj,  // So, first use the ... operator to get the prevState
    bar: { qux: {} } // then, update
  })
}

score:1

You can use UseReducer provided by react. It is very simple and it provide a very clean code.

import React, {useReducer} from 'react';

const initialState = {
    email: '',
    password: ''
};

function reducer(state, action) {
    switch (action.type) {
        case 'email':
            return {
                ...state,
                email: action.payload
            };
        case 'password':
            return {
                ...state,
                password: action.payload
            };
        default:
            throw new Error();
    }
}

const Login = () => {
    const [state, dispatch] = useReducer(reducer, initialState);
    const handleChange = event => dispatch({ type: event.target.name, payload: event.target.value.trim() });
    return (
        <div className="container">
            <div className="form-group">
                <input 
                    type="email"
                    name="email"
                    value={state.email}
                    onChange={handleChange}
                    placeholder="Enter Email"
                />
            </div>
            <div className="form-group">
                <input 
                    type="password"
                    name="password"
                    value={state.password}
                    onChange={handleChange}
                    placeholder="Enter Password"
                />
            </div>
            <div className="form-group">
                <button className="btn btn-info">Login</button>
            </div>
        </div>
    )
}

export default Login;

score:1

You can use prevState.

const [state, setState] = useState({ fruit: "", price: "" });

  const handleChange = e => {
    const { name, value } = e.target;
    setState(prevState => ({
      ...prevState,
      [name]: value
    }));
  };

<input
        value={state.fruit}
        type="text"
        onChange={handleChange}
        name="fruit"
      />
      <input
        value={state.price}
        type="text"
        onChange={handleChange}
        name="price"
      />

score:9

You could create a custom hook like so:

import { useState } from 'react';

export const useForm = (initialValues) => {
  const [values, setValues] = useState(initialValues);

  return {
    values,
    handleChange: (e) => {
      setValues({
        ...values,
        [e.target.name]: e.target.value,
      });
    },
    reset: () => setValues(initialValues),
  };
};

Then use it in any form (for example):

export default function SignInForm() {
    const { values, handleChange, reset } = useForm({ username: '', password: '' });

    const handleSubmit = e => {
        e.preventDefault();
        reset();
    };

    return (
        <form onSubmit={handleSubmit}>
            <input
                type='text'
                name='username'
                placeholder='Enter your username...'
                onChange={handleChange}
                value={values.username}
            />
            <input
                type='password'
                name='password'
                placeholder='Enter your password...'
                onChange={handleChange}
                value={values.password}
            />
            <button type='submit'>
                Sign In
            </button>
        </form>
    );
}

score:21

Thanks to Doğancan Arabacı. I tried to mimic the state and setState from class-based component so the feel doesn't change. My solution looks like this.

const [state , setState] = useState({
    email : "",
    password : ""
})

const handleChange = e => {
    const {name , value} = e.target
    setState( prevState => ({
        ...prevState,
        [name] : value
    }))
}

Related Query

More Query from same tag