Сброс в исходное состояние с помощью 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.info(data)
            clearState() // <-----------
        })
    }

    return <JSX />
}

export default Signup

Каждая часть состояния используется для контролируемого ввода формы.

По сути, то, что я хочу сделать, это после того, как пользователь успешно зарегистрировался, я хочу, чтобы состояние вернулось к исходному состоянию с очищенными полями.

Крайне важно вручную установить каждую часть состояния обратно в пустые строкиclearState Мне было интересно, есть ли метод или функция, поставляемая с React, которая сбрасывает состояние обратно к его начальным значениям?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
88
0
147 757
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Насколько я знаю (читая реактивные документы), пока нет способа сделать это.

Вы можете использовать одну переменную состояния, как описано в FAQ здесь: https://reactjs.org/docs/hooks-faq.html#should-i-use-one-or-many-state-variables

Конечно, это зависит от вашего варианта использования.

Смена ключа компонента из родительского контейнера, конечно, также автоматически сбросит его.

Привет, спасибо за ответ, мне было интересно, что вы подразумеваете под «повторным вводом»?

avatarhzh 27.02.2019 00:52

@avatarhzh Если вы измените ключ компонента, реакция размонтирует его и смонтирует как новый компонент. Не уверен, что это лучший подход в данном случае, так как вы можете потерять фокус и т. д.

jontro 27.02.2019 00:59
Ответ принят как подходящий

К сожалению, нет встроенного способа установить состояние в его начальное значение.

Ваш код выглядит хорошо, но если вы хотите уменьшить количество необходимых функций, вы можете поместить все состояние формы в один объект переменной состояния и сбросить его до исходного объекта.

Пример

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>

Наряду с другими ответами я бы порекомендовал подобрать вспомогательную библиотеку нравится или создать собственную абстракцию поверх хуков, если это то, что вы будете делать часто.

useState и друзья на самом деле являются просто низкоуровневыми примитивами для вас, пользователя, для создания более полезных крючков поверх них. У меня есть проекты, в которых необработанные useState звонки довольно редки.

Я полностью согласился с ответом @Tholle.

Если вам нужно запустить некоторые функции после очистки состояния

const clearState = () => {
  setState({...initialState});
  return {
    foo,
    bar,
    // ...
  };
};

// when component is unmounted

() => clearState().foo();
() => clearState().bar();

У меня был похожий вариант использования. Полностью не связан с механизмом входа в систему, регистрацией, но я изменил его, чтобы он был связан с вашим вариантом использования.

На мой взгляд, простой способ решить эту проблему — использовать родительский компонент.

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.info(data)
            resetUser(initUser) // <-----------
        })
    }

    return <JSX />
}

export default Signup

Добавьте комментарий, если вы понизите голос, чтобы улучшить этот ответ, по крайней мере, на ваш взгляд

Matthis Kohli 20.04.2020 12:11

Я думаю, что проголосовавший ответ все еще правильный, но недавно React выпустил новый встроенный useReducer, который, по их собственным словам,

handy for resetting the state later in response to an action

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

Также в нем говорится, что использованиеReducer обычно предпочтительнее, когда у вас есть сложная логика состояния, которая включает несколько подзначений или когда следующее состояние зависит от предыдущего.

Используя тот же образец в ответе, за который проголосовали, вы можете использовать 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 как можно более универсальной, но вы можете полностью изменить ее и протестировать различные типы действий (кроме простых имен свойств состояния) и выполнять сложные вычисления перед возвратом измененного состояния. В приведенной выше ссылке есть несколько примеров.

Я искал версию Typescript общего метода handleChangeEvent, и это идеально подходит. Отличный пример @Guilherme

tif 25.08.2020 21:20

Вот как вы можете сбросить входные значения (из объекта) в хуки после отправки формы.

Вы можете определить несколько входных значений в одном и том же 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 handleChangeEvent = e => {
    const { name, value } = e.target;
    setState({ ...state, [name]: value });
  };
  console.info(state)
  return (
    <form onSubmit = {handleSubmit}>
      <input
        type = "text"
        name = "firstName"
        placeholder = "Enter first name"
        value = {state.firstName}
        onChange = {handleChangeEvent}
      />
      <input
        type = "text"
        name = "lastName"
        placeholder = "Enter last name"
        value = {state.lastName}
        onChange = {handleChangeEvent}
      />
      <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 = "[email protected]">

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

 </form>

Интересный ответ. Выполняет ли отправка полную повторную отправку страницы по умолчанию, или myForm.current.reset() будет работать с флагом event.preventDefault, чтобы просто обновить соответствующую часть DOM? (т. е. увидит ли пользователь мигание экрана и полную перезагрузку страницы?)

zipzit 23.08.2020 07:35

Играл с этим... и это отлично работает. Подтверждаю, просто обновляется ключевой элемент DOM. См. codeandbox здесь.

zipzit 23.08.2020 08:17

Если вам нужен быстрый и грязный метод, вы можете попробовать просто изменить ключ компонента, что заставит React размонтировать ваш старый экземпляр компонента и смонтировать новый.

Я использую Lodash здесь для создания уникального одноразового идентификатора, но вы, вероятно, также можете обойтись 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/[email protected]/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 цента.

connect2Coder 24.07.2020 19:09

Есть время и место для быстрого и грязного. Этот метод пригодился в прошлом, и я хотел убедиться, что он полностью задокументирован здесь, поскольку у любого подхода есть свои плюсы и минусы. Этот простой, без внешних зависимостей, и работает с функциональными компонентами и компонентами на основе классов, даже если он кажется немного хакерским.

Eliot 24.07.2020 19:13

Короткий ответ

Это имеет очень простое решение. Вы можете изменить ключевую опору, где находится компонент рендеринга. например, когда у нас есть компонент для редактирования, мы можем передать другой ключ, чтобы очистить предыдущие состояния.

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

Спасибо @Masih, быстрое решение и работает отлично.

Prathamesh Talathi 02.01.2021 19:13

Осторожно: если вы зависите от всех случаев использования <Component /> для передачи реквизита key в качестве средства сброса внутреннего состояния, вы можете быть удивлены, когда вы или кто-то другой использует компонент и забывает включить key. Я знаю, что это официальная стратегия документации по реакции, но здесь легко ошибиться.

KFunk 21.08.2021 06:52

Я только что написал собственный хук, который возвращает настоящие хуки вместе с функцией 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];
};

Вы можете «обернуть» свой useState в другое использование [любое имя, которое вы хотите] и включить функцию сброса, то есть как пользовательский хук, как предложил Августин в своем ответе.

Взяв пример формы ввода, поскольку есть хороший реальный пример, который вы можете использовать и просмотреть источник, как указано ниже, вы должны использовать настраиваемый хук, подобный этому:

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 (на момент написания) — функция определена в исходном коде useForm, и есть пример ее использования в 'useForm.test.js':

Другие вопросы по теме