使用 React Hooks 重置为初始状态

Reset to Initial State with React Hooks

我目前正在制作注册表单,以下是我的代码片段:

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是否有一种方法或函数可以将状态重置回其初始值?

据我所知(通过阅读 React 文档)- 目前还没有办法这样做。

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

当然这取决于您的使用情况。

当然,从父容器重新键入组件也会自动重置它。

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

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

例子

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>

除了其他答案,我还建议您选择一个辅助库 like this,或者在挂钩之上创建您自己的抽象,如果这是您经常做的事情。

useState 和 friends 实际上只是低级原语,供您(用户)在其上构建更多有用的挂钩。我有一些项目,原始 useState 调用实际上并不常见。

我有一个类似的用例。完全与登录、注册机制无关,但我将其更改为与您的用例相关。

在我看来,解决这个问题的一个简单方法是使用父组件。

const initUser = {
  name: '',
  email: '',
  password: '',
  passwordConfirmation: ''      
}

const LoginManager = () => {
  const [user, setUser] = useState(initUser)

  return <Signup user={user} resetUser={setUser} />
}

const Signup = ({user, resetUser}) => {
    const [username, setUsername] = useState(user.name)
    const [email, setEmail] = useState(user.email)
    const [password, setPassword] = useState(user.password)
    const [passwordConfirmation, setPasswordConfirmation] = useState(user.passwordConfirmation)


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

    return <JSX />
}

export default Signup

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

handy for resetting the state later in response to an action

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;

打字稿

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 尽可能通用,但您可以完全更改它并测试不同的操作类型(除了简单地声明 属性 名称)并执行复杂的操作返回状态修改前的计算。上面提供的 link 中有一些示例。

这是在提交表单后在 hooks 中重置 input values(from object) 的方法。

您可以在同一个 useState 中定义多个输入值,例如 firstNamelastNameetc...

const [state, setState] = React.useState({ firstName: "", lastName: "" });

示例代码。

export default function App() {
  const [state, setState] = React.useState({ firstName: "", lastName: "" });
  const handleSubmit = e => {
    e.preventDefault();
    setState({firstName:'',lastName:''})
  };
  const handleChange = e => {
    const { name, value } = e.target;
    setState({ ...state, [name]: value });
  };
  console.log(state)
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        name="firstName"
        placeholder="Enter first name"
        value={state.firstName}
        onChange={handleChange}
      />
      <input
        type="text"
        name="lastName"
        placeholder="Enter last name"
        value={state.lastName}
        onChange={handleChange}
      />
      <input type="submit" value="Submit" />
    </form>
  );
}

If you want multiple input to define in object instead of declaring seperately.

你可以像这样在钩子中使用 useRef

 const myForm = useRef(null)

 const submit = () => {

   myForm.current.reset(); // will reset the entire form :)

   }

  <form ref={myForm} onSubmit={submit}>

   <input type="text" name="name" placeholder="John Doe">

     <input type="email" name="name" placeholder="usman@gmail.com">

     <button type="submit">Submit</button>

 </form>

如果你想要一个快速的 n' dirty 方法,你可以尝试只更改组件的键,这将导致 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>

简答

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

return <Component key={<different key>} />

我刚刚编写了一个 returns 实际挂钩的自定义挂钩,以及一个 resetState 函数。

用法:

const [{
    foo: [foo, setFoo],
    bar: [bar, setBar],
  },
  resetState,
] = useStateWithReset({
  foo: null,
  bar: [],
})

// - OR -

const [
    [foo, setFoo],
    [bar, setBar],
  ],
  resetState,
] = useStateWithReset([
  null,
  [],
])

后者可读性差,但前者重复密钥,所以没有完美的解决方案。

代码:

const useStateWithReset = initialState => {
  const hooksArray = Object.fromEntries(
    Object.entries(initialState).map(([k, v]) => {
      return [k, useState(v)]
    })
  );
  const resetState = () =>
    Object.entries(initialState).map(
      ([k, v]) => hooksArray[k][1](v)
    );
  return [hooksArray, resetState];
};

您可以 'wrap' 您的 useState 在另一个用途中使用 [无论您想要什么名称] 并包含一个重置功能 - 即像 Augustin 在他的回答中建议的自定义挂钩。

以输入表单为例,因为有一个很好的真实示例,您可以使用并查看下面提到的来源,您可以使用类似于这样的自定义挂钩:

function ContactForm(props) {
  const [state, handleSubmit, reset] = useForm("contactForm");

  const clearForm = e => {
    e.preventDefault();
    reset();  // <---- the extra reset function
    // Any other code you want like hiding 
    // or removing the form div from the 
    // DOM etc.
  }

  if (state.succeeded) {
      return (
        <React.Fragment>
          <p>Thanks fro your input!</p>
          <button className="default-button" onClick={clearForm}>Ok</button>
        </React.Fragment>
      );
  }
  return (
      <form onSubmit={handleSubmit}> // <-- the standard setSate type function
      <label htmlFor="email" className="form-element">
        Email Address
      </label>
      <input
        id="email"
        type="email"
        name="email"
        className="form-element"
      />
      // etc - Your form code...
      <button className="default-button" type="submit" disabled={state.submitting}>
        Submit
      </button>
    </form>
  );
}

您可以在 fomrspree git respoitory react 示例中看到这个操作(在撰写本文时)- 该函数在 useForm 源代码中定义,并且在 [=23] 中有一个使用示例=]:

实现此“将状态重置为初始值”的一种方法是使用 use-state-with-deps 程序包。

示例:

import {useStateWithDeps} from "use-state-with-deps";

const Signup = () => {
    const [generation, setGeneration] = useState(0);

    const [username, setUsername] = useStateWithDeps("", [generation])
    const [email, setEmail] = useStateWithDeps("", [generation])
    const [password, setPassword] = useStateWithDeps("", [generation])
    const [passwordConfirmation, setPasswordConfirmation] = useStateWithDeps("", [generation])

    const clearState = () => {
        setGeneration(generation + 1);
    }

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

    return <JSX />
}

export default Signup

如果你不想引入新的依赖,你可以在 this thread, which are short enough to just include directly in your project (eg. in a "utils" file). For example, this solution 中找到其他解决方案,只有 20 行。

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