обработка эффекта использования и изменений на стороне сервера

#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 пользователю во время ожидания.