#reactjs #typescript #jsx #native
#reactjs #typescript #jsx #native
Вопрос:
я создаю регистрационную форму и столкнулся с проблемой. После того, как пользователь заполняет поля, я выполняю проверку правильности с помощью регулярного выражения. Каждая функция проверки будет выдавать сообщение об ошибке. Даже если я намеренно вызываю все эти ошибки, при отправке формы появится только одна. Вот мой код:
function RegisterScreen(): JSX.Element{
const pwInput = React.createRef();
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [birthday, setBirthday] = useState<Date | null>(null);
const [isSelected, setSelection] = useState(false);
const [openTerms, setOpenTerms] = useState(false);
const [emailError, setEmailError] = useState<string | null>(null);
const [passError, setPassError] = useState<string | null>(null);
const [confPassError, setConfPassError] = useState<string | null>(null);
const [isTermsChecked, setIsTermsChecked] = useState<string | null>(null);
function _validateEmail(): boolean{
const re = /^(([^<>()[]\.,;:s@"] (.[^<>()[]\.,;:s@"] )*)|(". "))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9] .) [a-zA-Z]{2,}))$/;
if(re.test(email))
return true;
else{
setEmailError(RegisterErrors.emailError);
return false;
}
}
function _validatePw(): boolean{
const re = /^(?=.*d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/
if(re.test(password)) return true;
else{
setPassError(RegisterErrors.pwWeak);
return false
}
}
function _pwMatch(): boolean{
if(password == confirmPassword){
return true;
}else{
setConfPassError(RegisterErrors.pwConfirmMatchError);
return false;
}
}
function _validateAge(): boolean{
return true;
}
function _validateTermsAndCond(): boolean{
return true;
}
function validateFieldsAndRegister(){
console.log(email);
console.log(password);
console.log(confirmPassword);
if( _validateEmail() amp;amp; _validatePw() amp;amp; _validateAge() amp;amp; _validateTermsAndCond() amp;amp; _pwMatch()){
//trimite la firebase
console.log("okay")
}else{
console.log('wrong fields')
}
}
return(
<SafeAreaView style={{flex:1}}>
<ScrollView>
<View style = {{flex:1, alignItems: "center", justifyContent:"center",backgroundColor:"white"}}>
<TermsAndConditions open = {openTerms} close = { ()=>setOpenTerms(false) }/>
<Image source={require("../../images/logoR.png")} style={styles.logoImg}/>
<Text style={styles.mainText}>Register</Text>
<Input
containerStyle={styles.inputView}
inputContainerStyle={{backgroundColor:"white"}}
placeholder="First Name..."
placeholderTextColor="rgb(0,0,0)"
errorStyle={{ color: 'red' }}
onChangeText={text => setFirstName(text)}/>
<Input
containerStyle={styles.inputView}
placeholder="Last Name..."
placeholderTextColor="rgb(0,0,0)"
errorStyle={{ color: 'red' }}
onChangeText={text => setLastName(text)}/>
<Input
onFocus={()=>{setPassError(null)}}
secureTextEntry
containerStyle={styles.inputView}
placeholder="Password..."
placeholderTextColor="rgb(0,0,0)"
errorStyle={{ color: 'red' }}
errorMessage={passError}
onChangeText={text =>setPassword(text)}/>
<Input
secureTextEntry
onFocus = {()=>{setConfPassError(null)}}
containerStyle={styles.inputView}
placeholder="Confirm Password..."
placeholderTextColor="rgb(0,0,0)"
errorStyle={{ color: 'red' }}
errorMessage={confPassError}
onChangeText={text =>setConfirmPassword(text)}/>
<Input
containerStyle={styles.inputView}
onFocus={()=>{setEmailError(null)}}
placeholder="Email..."
placeholderTextColor="rgb(0,0,0)"
errorStyle={{ color: 'red' }}
errorMessage={emailError}
onChangeText={text => setEmail(text)}/>
<View style={styles.checkboxContainer}>
<CheckBox
value={isSelected}
onValueChange={()=>{setSelection(!isSelected)}}
style={styles.checkbox}
/>
<TouchableOpacity onPress = {()=> {setOpenTerms(true);}}>
<Text style={styles.label}>Terms and Conditions</Text>
</TouchableOpacity>
</View>
<TouchableOpacity style={{...styles.inputView, ...styles.regBtnContainer}} onPress = {()=>{validateFieldsAndRegister()}}>
<Text style = {styles.txtRegBtn}>Register</Text>
</TouchableOpacity>
</View>
</ScrollView>
</SafeAreaView>
)
}
Если я вызову функции проверки перед инструкцией if в VALIDATEFIELDS и Register, это сработает. В 3 полях будут отображаться ошибки.
Вот ошибки, которые я показываю
export const RegisterErrors = {
emailError: "You must enter a valid email",
pwConfirmMatchError:"The passwords you entered are not the same",
termsAndCondError:"You must read and agree with the Terms and Conditions",
birthdayError:"You must be 21 or older",
pwWeak:"Your password must contain: a lowercase letter, a acapital letter, a number and minimum 8 characters"
}
Комментарии:
1. Приняли ли вы во внимание асинхронный характер обновлений состояния? Это может быть просто состояние гонки.
2. похоже, что как только функция проверки возвращает false, остальные не выполняются, потому что в этом нет смысла .. общий результат будет false, поскольку я использую оператор AND . Есть ли такое в typescript / javascript?
3. Да, JS приведет к короткому замыканию.
4. Это особенность javascript для вычисления короткого замыкания
Ответ №1:
Вот фрагмент, иллюстрирующий часть того, что происходит. Это иллюстрирует три различные стратегии обновления состояния.
Первый группирует все три setState()
вызова в один вызов функции и не использует if()
инструкцию для проверки результатов. Изменения состояния отображаются немедленно.
Второй разделяет обновления на три отдельных вызова async
функций, которые, в свою очередь, await
перед вызовом имеют тайм-аут setState()
. Эти вызовы выполняются так, как это делает OP из if()
инструкции. Каждая асинхронная функция возвращает обещание, которое оценивается как истинное, и мы видим, что ‘promise’ регистрируется в консоли. Затем поступают результаты таймаутов.
Третий случай наиболее близок к вопросу OP. Он также выполняет отдельные вызовы из if()
инструкции, но они синхронны и возвращают false
. Первый вызов завершает свой setState()
вызов, возвращает false и останавливает дальнейшие вызовы изнутри if. Поскольку состояние изменилось, React вызывает визуализацию, и мы видим изменение одного состояния.
Результатом всего этого является то, что, по-видимому, лучше всего либо выполнить вызовы проверки перед if()
инструкцией, либо, что еще лучше, собрать все результаты проверки, а затем сгруппировать все setState()
вызовы в одну функцию. Это позволит React более эффективно группировать эти изменения и вызывать как можно меньше рендеров.
(извините за загроможденный фрагмент, встроенный babel, похоже, не справляется async/await
)
.container {
display: flex;
}
.example {
margin: 1rem;
padding: 1rem;
border: 1px solid gray;
}
button {
margin: 1rem;
}
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel">
const { useState } = React
function App() {
const [stateOne, setStateOne] = useState(1);
const [stateTwo, setStateTwo] = useState(1);
const [stateThree, setStateThree] = useState(1);
const cycleStateSingle = () => {
setStateOne((state) => state 1);
setStateTwo((state) => state 1);
setStateThree((state) => state 1);
}
const [stateOnePromise, setStateOnePromise] = useState(1);
const [stateTwoPromise, setStateTwoPromise] = useState(1);
const [stateThreePromise, setStateThreePromise] = useState(1);
const cycleOne = async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
setStateOnePromise((state) => state 1);
}
const cycleTwo = async () =>{
await new Promise(resolve => setTimeout(resolve, 2000));
setStateTwoPromise((state) => state 1);
}
const cycleThree = async () =>{
await new Promise(resolve => setTimeout(resolve, 3000));
setStateThreePromise((state) => state 1);
}
const cycleStatePromise = () => {
if (cycleOne(true) amp;amp; cycleTwo(true) amp;amp; cycleThree(true)) {
console.log('promise');
}
}
const [stateOneStepped, setStateOneStepped] = useState(1);
const [stateTwoStepped, setStateTwoStepped] = useState(1);
const [stateThreeStepped, setStateThreeStepped] = useState(1);
const cycleOneStepped = () => {
setStateOneStepped((state) => state 1);
return false;
}
const cycleTwoStepped = () =>{
setStateTwoStepped((state) => state 1);
return false;
}
const cycleThreeStepped = () =>{
setStateThreeStepped((state) => state 1);
return false;
}
const cycleStateStepped = () => {
if (cycleOneStepped() amp;amp; cycleTwoStepped() amp;amp; cycleThreeStepped()) {
console.log('stepped');
}
}
return (
<div className="container">
<div className="example">
<div className="state-1">State 1: {stateOne}</div>
<div className="state-2">State 2: {stateTwo}</div>
<div className="state-3">State 3: {stateThree}</div>
<button type="button" onClick={cycleStateSingle}>Single State Change</button>
</div>
<div className="example">
<div className="state-1">State 1: {stateOnePromise}</div>
<div className="state-2">State 2: {stateTwoPromise}</div>
<div className="state-3">State 3: {stateThreePromise}</div>
<button type="button" onClick={cycleStatePromise}>Promise State Change</button>
</div>
<div className="example">
<div className="state-1">State 1: {stateOneStepped}</div>
<div className="state-2">State 2: {stateTwoStepped}</div>
<div className="state-3">State 3: {stateThreeStepped}</div>
<button type="button" onClick={cycleStateStepped}>Stepped State Change</button>
</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
</script>
<div id="root"></div>