如何使用钩子通过一个事件处理函数动态更新任何输入字段的值

How to dynamically update the value for any input field with one event handler function, using hooks

TL;DR

Is there any way to dynamically update the value for input fields with one event handler function like we could do with stateful component.

我正在处理一个包含 2 个字段的登录表单。电子邮件和密码。当我使用代表这两个字段的 2 个 useState 时,当我使用 handleChange 更新状态时,两个状态都会更新。这不是本意。

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

    const handleChange = e => {
        const {value} = e.target
        setEmail(value)
        setPassword(value)
    }

我不想使用多个事件处理程序来处理每个输入字段。我试过了

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

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

但这会一次更新一个 属性。而另一个 属性 值丢失了。那么有什么方法可以像使用有状态组件那样使用一个事件处理函数来更新所有输入字段。

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

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

你应该使用带有回调函数的setState:

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

您将创建一个新的状态对象,该对象由先前的状态创建。你可以覆盖你需要的任何东西。 如果您有一个复杂的状态对象,您将需要更多新对象。

尝试

this.state = { 电子邮件 : ””, 密码 : ”” }

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

您可以像这样创建自定义挂钩:

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),
  };
};

然后以任何形式使用它(例如):

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>
    );
}

感谢 Dogancan Arabacı。我试图从基于 class 的组件中模仿状态和 setState,因此感觉不会改变。我的解决方案看起来像这样。

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

const handleChange = e => {
    const {name , value} = e.target
    setState( prevState => ({
        ...prevState,
        [name] : value
    }))
}
// 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
  })
}

可以使用react提供的UseReducer。它非常简单,并且提供了非常干净的代码。

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;

你可以使用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"
      />

试试这个:

  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;
    }
  };

您的输入:

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

创建所有输入的字典

您可以使用输入的 name 为对象(用作字典)中的每个输入保存 statesetState 的引用。

以下示例也适用于 checkbox 输入:

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;

创建自定义挂钩

此外,可以重构以上内容以创建可用于 forms/inputs 其他组件中的自定义挂钩:

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);