使用React hooks处理输入

157

我发现使用hooks处理用户文本输入的方法有几种。哪种方式更可取或更适当?你会选择哪种方式?

1)处理输入的最简单的钩子,但是你要处理的字段越多,你就需要编写重复的代码越多。

const [username, setUsername] = useState('');
const [password, setPassword] = useState('');

事件:

onChange={event => setPassword(event.target.value)}
onChange={event => setUsername(event.target.value)}

2)类似于上面的示例,但使用动态键名

const [inputValues, setInputValues] = useState({
  username: '', password: ''
});

const handleOnChange = event => {
  const { name, value } = event.target;
  setInputValues({ ...inputValues, [name]: value });
};

事件:

onChange={handleOnChange}

3) useReduceruseState的一种替代方案,且根据ReactJS文档所述,useReducer通常优于useState

const [inputValues, setInputValues] = useReducer(
  (state, newState) => ({ ...state, ...newState }),
  {username: '', password: ''}
);

const handleOnChange = event => {
  const { name, value } = event.target;
  setInputValues({ [name]: value });
};

事件:

onChange={handleOnChange}

4) useCallback 返回的是一个记忆化版本的回调函数,只有当其中一个依赖项发生变化时才会更改。

const [inputValues, setInputValues] = useState({ 
  username: '', password: '' 
});

const handleOnChange = useCallback(event => {
  const { name, value } = event.target;
  setInputValues({ ...inputValues, [name]: value });
});

事件:

onChange={handleOnChange}

3
没有第二个参数(依赖项数组)的情况下,useCallback 没有多大意义……在我看来,useReduce 对于对象而言比 useState 更加灵活且出错概率更小。 - Aprillion
7个回答

143

写一个可重复使用的函数,它返回输入值...和<input>本身:

 function useInput({ type /*...*/ }) {
   const [value, setValue] = useState("");
   const input = <input value={value} onChange={e => setValue(e.target.value)} type={type} />;
   return [value, input];
 }

那可以被用作:

 const [username, userInput] = useInput({ type: "text" });
 const [password, passwordInput] = useInput({ type: "text" });

 return <>
   {userInput} -> {username} <br />
   {passwordInput} -> {password}
 </>;

2
@JonathanAkweteyOkine 如果你在谈论第二个返回值,那就是 React Fragments 的简写语法。请参阅此处:https://reactjs.org/docs/fragments.html - Marco
1
在不使用任何钩子的函数中,是否有必要使用前缀use - Niyas Nazar
15
@Marco,“->”箭头只是被渲染为文本,不是任何特殊的语法。 - EmpireJones
2
@duhaime 是的,没错。这段代码片段并不是“复制和粘贴就能用”的,它旨在演示如何在最简单的情况下使用钩子。当然,你可以添加一个 useCallback 来处理这个问题。 - Jonas Wilms
8
请勿这样做。HTML 是一项珍贵的公共资源。将一个基本的 web 块抽象成这样的函数,然后用 useState 和解构雾化窗口,会让您不太有想象力的同事感到困惑,并使他们远离 HTML 神奇世界。100 票支持此答案让我觉得我可能在错误的企鹅群中。 - bbsimonbb
显示剩余5条评论

67

这是我目前的使用方式:

const [inputValue, setInputValue] = React.useState("");

const onChangeHandler = event => {
   setInputValue(event.target.value);
};

<input
   type="text"
   name="name"
   onChange={onChangeHandler}
   value={inputValue}
/>

3
我原本想评论这个。我感觉这是最简单的方法。此外,这也是Dan Abramov在这里介绍Hooks时演示的方式:https://www.youtube.com/watch?v=dpw9EHDh2bM @ 25:25 - I_am_learning_now
这是一种非常简单和有机的理解方式。谢谢。 - marcode_ely
2
你应该使用useCallback来包装onChangeHandler - Andreas Linnert
1
为什么选择@AndreasLinnert? - ncesar
1
@ncesar 嗯,更新一下:看起来这并不简单。仅仅调用 useCallback() 的成本比它在这种情况下节省的要高。它只有在与被包裹在 React.memo() 中的子组件一起使用时才有用。 - Andreas Linnert

41

你可以使用useState()来处理React Hooks。

import React, {useState} from 'react'

export default () => {
    const [fName, setfName] = useState('');
    const [lName, setlName] = useState('');
    const [phone, setPhone] = useState('');
    const [email, setEmail] = useState('');

const submitValue = () => {
    const frmdetails = {
        'First Name' : fName,
        'Last Name' : lName,
        'Phone' : phone,
        'Email' : email
    }
    console.log(frmdetails);
}

return(
    <>
    <hr/>
    <input type="text" placeholder="First Name" onChange={e => setfName(e.target.value)} />
    <input type="text" placeholder="Last Name" onChange={e => setlName(e.target.value)} />
    <input type="text" placeholder="Phone" onChange={e => setPhone(e.target.value)} />
    <input type="text" placeholder="Email" onChange={e => setEmail(e.target.value)} />
    <button onClick={submitValue}>Submit</button>
    </>
    )
}

4
这里是我的做法(假设您的输入必须在表单中):
我使用 BasicForm 组件。
它将所有输入状态存储到一个对象中,只需调用一次 useState()。
它通过 useContext() 传递 inputs 状态,并提供 onChange() 函数和 setInputInitialState() 函数,以便输入在首次挂载时设置其初始状态。它还传递了 onFocus、onBlur,并且有函数来验证字段,这里为了简化代码而不显示。
这样,我可以轻松创建一个包含任意数量输入的表单,例如:
<BasicForm
      isSubmitting={props.isSubmitting}
      submitAction={ (formState) =>
        props.doSignIn(formState) }
    >
      <TextInput
        type='email'
        label='Email'
        name='email'
        placeholder='Enter email...'
        required
      />
      <TextInput
        type='password'
        label='Password'
        name='password'
        placeholder='Enter password...'
        min={6}
        max={12}
        required
      />
      <SubmitButton
        label='Login'
      />
    </BasicForm>

BasicForm.js

import FormContext from './Parts/FormContext';

function BasicForm(props) {

  const [inputs, setInputs] = useState({});

  function onChange(event) {
    const newValue = event.target.value;
    const inputName = event.target.name;
    setInputs((prevState)=> {
      return({
        ...prevState,
        [inputName]: {
          ...prevState[inputName],
          value: newValue,
          dirty: true
        }
      });
    });
  }

  function setInputInitialState(
    inputName,
    label='This field ',
    type,
    initialValue = '',
    min = false,
    max = false,
    required = false) {

    const INITIAL_INPUT_STATE = {
      label: label,
      type: type,
      onFocus: false,
      touched: false,
      dirty: false,
      valid: false,
      invalid: false,
      invalidMsg: null,
      value: initialValue,
      min: min,
      max: max,
      required: required
    };

    setInputs((prevState) => {
      if (inputName in prevState) {
        return prevState;
      }
      return({
        ...prevState,
        [inputName]: INITIAL_INPUT_STATE
      });
    });

  }

return(
    <FormContext.Provider value={{
      onChange: onChange,
      inputs: inputs,
      setInputInitialState: setInputInitialState,
    }}>
      <form onSubmit={onSubmit} method='POST' noValidate>
        {props.children}
      </form>
    </FormContext.Provider>
  );
}

TextInput.js

在挂载时,输入框使用 useEffect() 钩子来设置它们的初始状态。

function TextInput(props) {

  const formContext = useContext(FormContext);

  useEffect(() => {
    console.log('TextInput useEffect...');
    formContext.setInputInitialState(
      props.name,
      props.label,
      props.type,
      props.initialValue,
      props.min,
      props.max,
      props.required
    );
  },[]);

  return(
      <input
        type={props.type}
        id={props.name}
        name={props.name}
        placeholder={props.placeholder}
        value={([props.name] in formContext.inputs) ?
                  formContext.inputs[props.name].value
                : props.initialValue || ''}
        onChange={formContext.onChange}
        onFocus={formContext.onFocus}
        onBlur={formContext.onBlur}
      >
      </input>
      </div>
      {([props.name] in formContext.inputs) ?
          formContext.inputs[props.name].invalidMsg && <div><span> {formContext.inputs[props.name].invalidMsg}</span></div>
        : null}
    </div>
  );

...
}

3
function App(){
    const [name, setName] = useState("");
    const [istrue, Setistrue] = useState(false);
    const [lastname,setLastname]=useState("");
    
    function handleclick(){
       Setistrue(true);
    }
    
    return(
        <div>
            {istrue ? <div> <h1>{name} {lastname}</h1> </div> : 
            <div>
                <input type="text" placeholder="firstname" name="name" onChange={e =>setName(e.target.value)}/>
                <input type="text" placeholder="lastname" name="lastname" onChange={e =>setLastname(e.target.value)}/>
               <button  type="submit" onClick={handleclick}>submit</button>
            </div>}
        </div>
    )
    
    }

}

1

你可能想考虑使用一个表单库,比如Formik


1
这正是React文档中提到的内容:https://reactjs.org/docs/forms.html#fully-fledged-solutions - Laiacy
我正要建议同样的事情...为什么这个答案会被踩呢? - Mohamed Iqzas

0

非常感谢您上面的答案和解释: 我使用Flask和React Hook实现了这些功能,使用了三个按钮(从服务器开始搜索数据,更新ag-grid表格和单元格数据以及将数据保存到服务器)与Ag-Grid。 我将其放在版本106中。 https://github.com/peterhchen/900_ReactJS_Flask_FullStack


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接