В настоящее время я работаю над формой регистрации, и ниже приведен фрагмент моего кода:
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, которая сбрасывает состояние обратно к его начальным значениям?





Насколько я знаю (читая реактивные документы), пока нет способа сделать это.
Вы можете использовать одну переменную состояния, как описано в FAQ здесь: https://reactjs.org/docs/hooks-faq.html#should-i-use-one-or-many-state-variables
Конечно, это зависит от вашего варианта использования.
Смена ключа компонента из родительского контейнера, конечно, также автоматически сбросит его.
@avatarhzh Если вы измените ключ компонента, реакция размонтирует его и смонтирует как новый компонент. Не уверен, что это лучший подход в данном случае, так как вы можете потерять фокус и т. д.
К сожалению, нет встроенного способа установить состояние в его начальное значение.
Ваш код выглядит хорошо, но если вы хотите уменьшить количество необходимых функций, вы можете поместить все состояние формы в один объект переменной состояния и сбросить его до исходного объекта.
Пример
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
Добавьте комментарий, если вы понизите голос, чтобы улучшить этот ответ, по крайней мере, на ваш взгляд
Я думаю, что проголосовавший ответ все еще правильный, но недавно React выпустил новый встроенный useReducer, который, по их собственным словам,
handy for resetting the state later in response to an action
https://reactjs.org/docs/hooks-reference.html#usereducer
Также в нем говорится, что использованиеReducer обычно предпочтительнее, когда у вас есть сложная логика состояния, которая включает несколько подзначений или когда следующее состояние зависит от предыдущего.
Используя тот же образец в ответе, за который проголосовали, вы можете использовать useReducer следующим образом:
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
Вот как вы можете сбросить входные значения (из объекта) в хуки после отправки формы.
Вы можете определить несколько входных значений в одном и том же 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? (т. е. увидит ли пользователь мигание экрана и полную перезагрузку страницы?)
Играл с этим... и это отлично работает. Подтверждаю, просто обновляется ключевой элемент DOM. См. codeandbox здесь.
Если вам нужен быстрый и грязный метод, вы можете попробовать просто изменить ключ компонента, что заставит 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 цента.
Есть время и место для быстрого и грязного. Этот метод пригодился в прошлом, и я хотел убедиться, что он полностью задокументирован здесь, поскольку у любого подхода есть свои плюсы и минусы. Этот простой, без внешних зависимостей, и работает с функциональными компонентами и компонентами на основе классов, даже если он кажется немного хакерским.
Это имеет очень простое решение. Вы можете изменить ключевую опору, где находится компонент рендеринга. например, когда у нас есть компонент для редактирования, мы можем передать другой ключ, чтобы очистить предыдущие состояния.
return <Component key = {<different key>} />
Спасибо @Masih, быстрое решение и работает отлично.
Осторожно: если вы зависите от всех случаев использования <Component /> для передачи реквизита key в качестве средства сброса внутреннего состояния, вы можете быть удивлены, когда вы или кто-то другой использует компонент и забывает включить key. Я знаю, что это официальная стратегия документации по реакции, но здесь легко ошибиться.
Я только что написал собственный хук, который возвращает настоящие хуки вместе с функцией 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':
Привет, спасибо за ответ, мне было интересно, что вы подразумеваете под «повторным вводом»?