#reactjs #react-native
#reactjs #react-native
Вопрос:
Я хочу подтвердить, что у нового пользователя есть уникальный ник или нет. И если он не уникален, я хочу показать сообщение пользователю. По этой причине я написал этот код:
const [vNicknameServer, setvNicknameServer] = useState(true);
…
const validateNicknameServer = async (nickname) => {
let flag = 0;
await firebase
.firestore()
.collection("Users")
.where("nickname", "==", nickname)
.get()
.then((querySnapshot) => {
querySnapshot.forEach((documentSnapshot) => {
if (documentSnapshot.data().nickname == nickname) {
setvNicknameServer(false); //Changing state
flag = 1;
}
});
if (flag == 0) {
setvNicknameServer(true); // changing state
}
})
.catch((err) => console.log(err));
};
….
{vNickname ? (
<Text style={{ color: "#DC8989" }}>
Nickname min 4 max 14 caracters
</Text>
) : vNicknameServer == false ? ( // according to state it appears or not
<Text style={{ color: "#DC8989" }}>Nickname must be unique</Text>
) : null}
Но процесс рендеринга всегда выполняется после какого-либо другого процесса. Я должен использовать useEffect, но я немного запутался, как его использовать там? Кто-нибудь может помочь мне использовать его?
Примечание:
Эта проверка была выполнена в обработчике отправки :
const Submit = () => {
validateEmail(email) ? setvEmail(false) : setvEmail(true);
validateNickname(nickname) ? setvNickname(false) : setvNickname(true);
validatePassword(password) ? setvPassword(false) : setvPassword(true);
validateNicknameServer(nickname);
console.log(vEmailServer);
console.log(email);
if (
validateEmail(email) amp;amp;
validateNickname(nickname) amp;amp;
validatePassword(password) amp;amp;
vNicknameServer amp;amp;
vEmailServer
) {
navigation.navigate("UserCredential", {
email: email,
password: password,
nickname: nickname,
});
}
};
Я хочу контролировать это только тогда, когда я нажимал кнопку не в каждом случае.
ПРИМЕЧАНИЕ: Пожалуйста, проверьте приведенный ниже ответ и комментарии.
Ответ №1:
Если я правильно понял ваш вопрос, вы пытаетесь использовать асинхронность в useEffect
перехвате. Для этого вы можете использовать следующий шаблон
// probably need to setup a useState hook to contain nickname data
const [nickname, setNickname] = useState(true);
useEffect(() => {
// get nickname from the component state instead of passing the data as a param
const validateNicknameServer = async () => {
let flag = 0;
await firebase
.firestore()
.collection("Users")
.where("nickname", "==", nickname)
.get()
.then((querySnapshot) => {
querySnapshot.forEach((documentSnapshot) => {
if (documentSnapshot.data().nickname == nickname) {
setvNicknameServer(false); //Changing state
flag = 1;
}
});
if (flag == 0) {
setvNicknameServer(true); // changing state
}
})
.catch((err) => console.log(err));
};
validateNicknameServer();
}, [nickname])
Обновление на основе примечания
Я не могу проверить, работает ли он без каких-либо ошибок. Но, надеюсь, это даст вам несколько идей для решения проблемы.
const validateNicknameServer = async (nickname) => {
let flag = 0;
const querySnapshot = await firebase
.firestore()
.collection("Users")
.where("nickname", "==", nickname)
.get()
.catch((err) => console.log(err));
querySnapshot.forEach((documentSnapshot) => {
if (documentSnapshot.data().nickname == nickname) {
setvNicknameServer(false); //Changing state
flag = 1;
return false
}
});
if (flag == 0) {
setvNicknameServer(true); // changing state
return true
}
};
const Submit = async () => {
// .... other code
// validate the name before navigation happens
// and use vNicknameServer to displaying messages
const isValidName = await validateNicknameServer(nickname);
// .... other code
if (
validateEmail(email) amp;amp;
validateNickname(nickname) amp;amp;
validatePassword(password) amp;amp;
isValidName amp;amp;
vEmailServer
) {
navigation.navigate("UserCredential", {
email: email,
password: password,
nickname: nickname,
});
}
};
Комментарии:
1. Можете ли вы проверить редактирование, в которое я добавил еще несколько сведений
2. Я думаю, что, но если я сделал обработчик асинхронным, то при каждом нажатии кнопка отвечает асинхронно, не так ли?
3. @LeoS да. ответ будет асинхронным. Мы ждем
validateNicknameServer
завершения, прежде чем решим, следует ли выполнять навигацию. Это поведение, которого вы не хотите?
Ответ №2:
Я думаю, вам не нужно использовать useEffect
, и for in
вместо этого используйте forEach
для удобства управления потоком.
const validateNicknameServer = async (nickname) => {
await firebase
.firestore()
.collection("Users")
.where("nickname", "==", nickname)
.get()
.then((querySnapshot) => {
for (var documentSnapshot in querySnapshot) {
if (documentSnapshot.data().nickname == nickname) {
return false;
}
}
return true;
})
.catch((err) => console.log(err));
};
const Submit = async () => {
// Other validation ...
// Waiting validate nickname on server. Should display loading state for better UX.
const validateNicknameServer = await validateNicknameServer(nickname);
// Change state after validate
setvNicknameServer(false);
// Form valid -> Navigate to UserCredential screen
if (
validateEmail(email) amp;amp;
validateNickname(nickname) amp;amp;
validatePassword(password) amp;amp;
vEmailServer
validateNicknameServer amp;amp;
) {
navigation.navigate("UserCredential", {
email: email,
password: password,
nickname: nickname,
});
}
};
Ответ №3:
При нажатии кнопки отправки вы выполняете асинхронную операцию, но не ждете ее, вот почему
процесс рендеринга всегда выполняется после какого-либо другого процесса
Таким образом, код метода отправки if будет выполнен до завершения асинхронной операции validateNicknameServer.
Я думаю, вам не нужно использовать useEffect здесь. также после setvNicknameServer() весь компонент будет повторно отображаться, поэтому метод Submit по-прежнему будет иметь предыдущее значение vNicknameServer, даже если вы его обновили.
Вот что вы можете сделать
Для метода validateNicknameServer при использовании await вы должны использовать try / catch вместо then / catch
const validateNicknameServer = async (nickname) => {
let flag = 0;
try {
const querySnapshot = await firebase
.firestore()
.collection('Users')
.where('nickname', '==', nickname)
.get();
querySnapshot.forEach((documentSnapshot) => {
if (documentSnapshot.data().nickname == nickname) {
flag = 1;
}
});
return flag == 0 ? true : false;
} catch (err) {
console.log(err);
}
return false;
};
теперь в методе отправки дождитесь завершения validateNicknameServer и то же самое для других методов (validateEmail, validateNickname, validatePassword), если они также асинхронны.
const Submit = async () => {
validateEmail(email) ? setvEmail(false) : setvEmail(true);
validateNickname(nickname) ? setvNickname(false) : setvNickname(true);
validatePassword(password) ? setvPassword(false) : setvPassword(true);
// wait for this method to complete, same for above methods too if they are async
const isNickNameUnique = await validateNicknameServer(nickname);
setvNicknameServer(isNickNameUnique);
console.log(vEmailServer);
console.log(email);
if (
validateEmail(email) amp;amp;
validateNickname(nickname) amp;amp;
validatePassword(password) amp;amp;
isNickNameUnique amp;amp;
vEmailServer
) {
navigation.navigate('UserCredential', {
email: email,
password: password,
nickname: nickname,
});
}
};
ожидается, что здесь операции такого типа будут асинхронными, поэтому рекомендуется показывать LoadingIndicator пользователю во время ожидания.