使用React Hooks重置为初始状态

时间:2019-02-26 23:39:10

标签: reactjs 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是否提供了一种方法或函数来将状态重置为其初始值?

12 个答案:

答案 0 :(得分: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>

答案 1 :(得分:4)

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

  

方便以后在响应动作时重置状态

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

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

在投票答案上使用相同的示例,您可以像这样使用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创建为尽可能通用,但是您可以完全更改它并测试不同的操作类型(除了简单的状态属性名称)并在返回修改后的状态之前执行复杂的计算。上面提供的链接中有一些示例。

答案 2 :(得分:4)

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

我在这里使用Lodash来生成唯一的一次性ID,但假设所需的时间分辨率在1毫秒以上,那么您也可以放弃public static Action<TimeSpan, CancellationToken> Sleep = (timeSpan, cancellationToken) => { if (cancellationToken.WaitHandle.WaitOne(timeSpan)) cancellationToken.ThrowIfCancellationRequested(); }; 或类似的东西。

我第二次以Date.now()的身份传递密钥,以便更轻松地了解发生了什么,但这不是必需的。

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

答案 3 :(得分:2)

据我所知(通过阅读react docs)-目前尚无办法。

答案 4 :(得分:2)

您本可以在钩子中使用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>

答案 5 :(得分:2)

我刚刚编写了一个返回实际钩子的自定义钩子,以及一个 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];
};

答案 6 :(得分:1)

您可以按照FAQ中的说明使用一个状态变量:https://reactjs.org/docs/hooks-faq.html#should-i-use-one-or-many-state-variables

这当然取决于您的用例。

从父容器中重命名组件当然也会自动将其重置。

答案 7 :(得分:1)

除了其他答案,我建议您选择一个帮助程序库like this,或者如果需要经常做的话,可以在钩子上进行自己的抽象。

useState和朋友实际上只是低级原语,对于您(用户)而言,它可以在其上构建更多有用的钩子。我有一些项目,其中原始useState调用实际上很少见。

答案 8 :(得分:1)

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

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

答案 9 :(得分:0)

我完全同意@Tholle的回答。

如果在清除状态后需要运行某些功能

String line = "2019 May 22 03:32:17.952296 france1v4 sh[4937]: 190522-03:32:17.951792 [mod=REC, lvl=INFO] [tid=26130] Recording A8602096210405800406L200218680503121519 size is 4145956224 bytes";
      String pattern = "^.+\\.(\\d+)";

      // Create a Pattern object
      Pattern r = Pattern.compile(pattern);

      // Now create matcher object.
      Matcher m = r.matcher(line);
      if (m.find( )) {
          System.out.println("Found value: " + m.group(1) ); //This would give 951792
              }else {
         System.out.println("NO MATCH");
      }

答案 10 :(得分:0)

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

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

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

答案 11 :(得分:-1)

提交表单后,您可以通过这种方法在钩子中重置输入值(来自对象)

您可以在同一useState中定义多个输入值,例如名字姓氏 ...

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

如果要在对象中定义多个输入而不是单独声明。