使用 React Hooks 重置为初始状态

IT技术 reactjs react-hooks
2021-04-06 16:03:30

我目前正在处理一个注册表单,以下是我的代码片段:

const Signup = () => {
    const [username, setUsername] = useState('')
    const [email, setEmail] = useState('')
    const [password, setPassword] = useState('')
    const [passwordConfirmation, setPasswordConfirmation] = useState('')

    const clearState = () => {
        setUsername('')
        setEmail('')
        setPassword('')
        setPasswordConfirmation('')
    }

    const handleSubmit = signupUser => e => {
        e.preventDefault()
        signupUser().then(data => {
            console.log(data)
            clearState() // <-----------
        })
    }

    return <JSX />
}

export default Signup

每个状态用于表单的受控输入。

基本上我想做的是在用户成功注册后,我希望状态回到初始状态并清除字段。

clearState我想知道是否有 React 附带的方法或函数将状态重置回其初始值时,手动将每个状态设置回空字符串是非常必要的

6个回答

遗憾的是,没有内置的方法可以将状态设置为其初始值。

您的代码看起来不错,但是如果您想减少所需的功能,您可以将整个表单状态放在单个状态变量对象中并重置为初始对象。

例子

const { useState } = React;

function signupUser() {
  return new Promise(resolve => {
    setTimeout(resolve, 1000);
  });
}

const initialState = {
  username: "",
  email: "",
  password: "",
  passwordConfirmation: ""
};

const Signup = () => {
  const [
    { username, email, password, passwordConfirmation },
    setState
  ] = useState(initialState);

  const clearState = () => {
    setState({ ...initialState });
  };

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

  const handleSubmit = e => {
    e.preventDefault();
    signupUser().then(clearState);
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>
          Username:
          <input value={username} name="username" onChange={onChange} />
        </label>
      </div>
      <div>
        <label>
          Email:
          <input value={email} name="email" onChange={onChange} />
        </label>
      </div>
      <div>
        <label>
          Password:
          <input
            value={password}
            name="password"
            type="password"
            onChange={onChange}
          />
        </label>
      </div>
      <div>
        <label>
          Confirm Password:
          <input
            value={passwordConfirmation}
            name="passwordConfirmation"
            type="password"
            onChange={onChange}
          />
        </label>
      </div>
      <button>Submit</button>
    </form>
  );
};

ReactDOM.render(<Signup />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

我认为投票的答案仍然是正确的,但最近 React 发布了新的内置useReducer函数,用他们自己的话来说,就是

便于稍后响应操作重置状态

https://reactjs.org/docs/hooks-reference.html#usereducer

它还指出,当您具有涉及多个子值的复杂状态逻辑或下一个状态取决于前一个状态时,通常最好使用 useReducer。

在投票答案上使用相同的示例,您可以像这样使用 useReducer:

Javascript

import React, { useReducer } from "react";

const initialState = {
    username: "",
    email: "",
    password: "",
    passwordConfirmation: "",
};

const reducer = (state, action) => {
    if (action.type === "reset") {
        return initialState;
    }

    const result = { ...state };
    result[action.type] = action.value;
    return result;
};

const Signup = () => {
    const [state, dispatch] = useReducer(reducer, initialState);
    const { username, email, password, passwordConfirmation } = state;

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

        /* fetch api */

        /* clear state */
        dispatch({ type: "reset" });
    };

    const onChange = e => {
        const { name, value } = e.target;
        dispatch({ type: name, value });
    };

    return (
        <form onSubmit={handleSubmit}>
            <div>
                <label>
                    Username:
                    <input value={username} name="username" onChange={onChange} />
                </label>
            </div>
            <div>
                <label>
                    Email:
                    <input value={email} name="email" onChange={onChange} />
                </label>
            </div>
            <div>
                <label>
                    Password:
                    <input
                        value={password}
                        name="password"
                        type="password"
                        onChange={onChange}
                    />
                </label>
            </div>
            <div>
                <label>
                    Confirm Password:
                    <input
                        value={passwordConfirmation}
                        name="passwordConfirmation"
                        type="password"
                        onChange={onChange}
                    />
                </label>
            </div>
            <button>Submit</button>
        </form>
    );
};

export default Signup;

typescript

import React, { FC, Reducer, useReducer } from "react";

interface IState {
    email: string;
    password: string;
    passwordConfirmation: string;
    username: string;
}

interface IAction {
    type: string;
    value?: string;
}

const initialState: IState = {
    email: "",
    password: "",
    passwordConfirmation: "",
    username: "",
};

const reducer = (state: IState, action: IAction) => {
    if (action.type === "reset") {
        return initialState;
    }

    const result: IState = { ...state };
    result[action.type] = action.value;
    return result;
};

export const Signup: FC = props => {
    const [state, dispatch] = useReducer<Reducer<IState, IAction>, IState>(reducer, initialState, () => initialState);
    const { username, email, password, passwordConfirmation } = state;

    const handleSubmit = (e: React.FormEvent) => {
        e.preventDefault();

        /* fetch api */

        /* clear state */
        dispatch({ type: "reset" });
    };

    const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        const { name, value } = e.target;
        dispatch({ type: name, value });
    };

    return (
        <form onSubmit={handleSubmit}>
            <div>
                <label>
                    Username:
                    <input value={username} name="username" onChange={onChange} />
                </label>
            </div>
            <div>
                <label>
                    Email:
                    <input value={email} name="email" onChange={onChange} />
                </label>
            </div>
            <div>
                <label>
                    Password:
                    <input
                        value={password}
                        name="password"
                        type="password"
                        onChange={onChange}
                    />
                </label>
            </div>
            <div>
                <label>
                    Confirm Password:
                    <input
                        value={passwordConfirmation}
                        name="passwordConfirmation"
                        type="password"
                        onChange={onChange}
                    />
                </label>
            </div>
            <button>Submit</button>
        </form>
    );
};

请注意,我创建了这个reducer函数 const 以使其尽可能通用,但是您可以完全更改它并测试不同的操作类型(除了简单的状态属性名称)并在返回修改后的状态之前执行复杂的计算。上面提供的链接中有一些示例。

我一直在寻找通用 handleChange 方法的 Typescript 版本,这非常适合。很好的例子@Guilherme
2021-06-14 16:03:30

这有一个非常简单的解决方案。您可以更改渲染组件所在的关键props。例如,当我们有一个要编辑的组件时,我们可以传递一个不同的键来清除以前的状态。

return <Component key={<different key>} />
感谢@Masih,快速解决方案并且完美运行。
2021-06-03 16:03:30
注意:如果您依赖于所有用法<Component />来传递keyprop 作为重置内部状态的手段,那么当您或其他人使用该组件而忘记包含 时,您可能会感到惊讶key我知道这是 react 文档的官方策略,但在这里很容易出错。
2021-06-16 16:03:30

如果你想要一个快速和肮脏的方法,你可以尝试改变组件的键,这将导致 React 卸载旧的组件实例并安装一个新的。

我在这里使用 Lodash 来生成一个唯一的一次性 ID,但Date.now()假设所需的时间分辨率超过 1 毫秒,您也可以使用或类似的方法。

我第二次传递密钥debugKey,以便更容易看到发生了什么,但这不是必需的。

const StatefulComponent = ({ doReset, debugKey }) => {
  const [counter, setCounter] = React.useState(0);
  const increment = () => setCounter(prev => prev + 1); 
  return (
    <React.Fragment>
      <p>{`Counter: ${counter}`}</p>
      <p>{`key=${debugKey}`}</p>
      <button onClick={increment}>Increment counter</button>
      <button onClick={doReset}>Reset component</button>
    </React.Fragment>
  );
};

const generateUniqueKey = () => `child_${_.uniqueId()}`;

const App = () => {
  const [childKey, setChildKey] = React.useState(generateUniqueKey());
  const doReset = () => setChildKey(generateUniqueKey());
  return (
    <div className="App">
      <StatefulComponent key={childKey} debugKey={childKey} doReset={doReset} />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  rootElement
);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>


<div id="root"></div>

这绝对是非常肮脏的艾略特,只要有办法,我不会推荐任何人使用这种方法。只有我的 2 美分。
2021-06-09 16:03:30
有一个时间和地点可以快速变脏。这种方法在过去派上用场,我想确保它在此处得到完整记录,因为任何方法都有优点和缺点。这个很简单,没有外部依赖性,并且可以与基于函数和类的组件一起使用,即使它感觉有点 hacky。
2021-06-13 16:03:30

您可以使用此处的常见问题解答中所述的一个状态变量:https : //reactjs.org/docs/hooks-faq.html#should-i-use-one-or-many-state-variables

当然,这取决于您的用例。

从父容器重新生成组件的密钥当然也会自动重置它。

您好,谢谢您的回答,我想知道您所说的“重新生成密钥”是什么意思?
2021-05-23 16:03:30
@avatarhzh 如果您更改组件上的密钥,react 将卸载它并将其安装为新组件。不确定在这种情况下这是否是最佳方法,因为您可能会失去焦点等。
2021-06-18 16:03:30