You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Firebase如何高效实现用户名存在性检查?

问题描述

我正在开发一款支持游客通过用户名登录游玩的游戏。原本的实现逻辑为:用户访问网站时,从Firebase获取所有用户数据并赋值给Redux的users状态,当用户点击Play按钮时检查用户名是否已存在。但该方式效率较低,我希望改为点击Play按钮时再获取用户数据。尝试通过async/await实现此逻辑时,出现了检查用户名前users数组始终为空的问题,导致已存在的用户名仍能登录。


使用Firebase获取用户数据的函数实现

import { async } from "@firebase/util";
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { collection, query, where, getDocs } from "firebase/firestore";
import { db } from '../firebase/auth';

export interface User{
    isUser: boolean;
    id: number;
    username: string
}

interface UserState{
    users:User[];
    isLoading:boolean;
    errorMessage:string;

}

export const fetchUsers = createAsyncThunk("users/fetchUsers", async()=>{
    const usersArray:User[] = [];
    const usersRef = collection(db, "users");
    const q = query(usersRef, where("isUser", "==", true));
    const querySnapshot = await getDocs(q);
    querySnapshot.forEach(doc =>{
        const user = doc.data() as User;
        usersArray.push(user);
    })

    return usersArray;
})

const initialState: UserState = {
    users:[],
    isLoading:false,
    errorMessage:""
}

const usersSlice = createSlice({
    name:"users",
    initialState,
    reducers:{},
    extraReducers:(builder)=>{
        builder.addCase(fetchUsers.pending, (state, action)=>{
            state.isLoading = true;
        })
        builder.addCase(fetchUsers.fulfilled, (state, action)=>{
            state.isLoading = false;
            state.users = action.payload;
            state.errorMessage = "";
        })
        builder.addCase(fetchUsers.rejected, (state, action)=>{
            state.isLoading = false;
            state.users = [];
            if(typeof action.error.message === "string" ){
                state.errorMessage = action.error.message;
            }
            
        })
    }
})

export const usersActions = usersSlice.actions;
export const userReducer = usersSlice.reducer

初始渲染时获取数据的原代码

useEffect(() => {
  dispatch(guestInputActions.changeInputValue(randomUser));
  dispatch(fetchUsers()); 

}, []); 

原用户名存在检查逻辑

const handleGuestLogin =  () => {
    async function checkIfUsernameExist() {
      if (users) {
        const existUser = users.find((user: User) => user.username === inputValue.toLowerCase());
        if (existUser) {
          dispatch(userLoginActions.setLoginStatus(false));
          dispatch(userLoginActions.setErrorMessage("This username already exists!"));
          return;
        } else {
          dispatch(userLoginActions.checkUserName(inputValue));

        }
      }
    }
 
   checkIfUsernameExist();

  };

修改后的代码(点击Play时获取数据)

修改后的useEffect代码

useEffect(() => {
    dispatch(guestInputActions.changeInputValue(randomUser));
  // dispatch(fetchUsers()) 注释掉该行

}, [randomUser]);

修改后的登录处理代码

const handleGuestLogin = async () => {
    async function checkIfUsernameExist() {
      if (users) {
        const existUser = users.find((user: User) => user.username === inputValue.toLowerCase());
        if (existUser) {
          dispatch(userLoginActions.setLoginStatus(false));
          dispatch(userLoginActions.setErrorMessage("This username already exists!"));
          return;
        } else {
          dispatch(userLoginActions.checkUserName(inputValue));

        }
      }
    }
   const val = await dispatch(fetchUsers());
   checkIfUsernameExist();

  };

问题原因与解决办法

问题原因

调用await dispatch(fetchUsers())后,Redux状态更新是异步的,组件不会立刻重新渲染,此时users还是初始的空数组,导致检查逻辑无法正确判断用户名是否存在。

方案一:直接使用thunk返回值判断

利用dispatch(fetchUsers())返回的Promise,通过unwrap()获取thunk成功后的用户数组,直接用这个数组做检查,无需依赖组件状态:

const handleGuestLogin = async () => {
  try {
    // 等待fetchUsers完成,取出返回的用户数组
    const users = await dispatch(fetchUsers()).unwrap();
    const existUser = users.find((user: User) => user.username === inputValue.toLowerCase());
    
    if (existUser) {
      dispatch(userLoginActions.setLoginStatus(false));
      dispatch(userLoginActions.setErrorMessage("This username already exists!"));
      return;
    }
    
    dispatch(userLoginActions.checkUserName(inputValue));
  } catch (error) {
    // 处理数据获取失败的情况
    dispatch(userLoginActions.setLoginStatus(false));
    dispatch(userLoginActions.setErrorMessage("获取用户数据失败,请重试"));
  }
};

方案二:监听状态变化触发检查

如果需要依赖组件的users状态,添加状态标记控制检查时机,通过useEffect监听数据加载完成事件:

import { useState, useEffect } from 'react';

// 组件内添加状态标记是否需要检查用户名
const [shouldCheckUsername, setShouldCheckUsername] = useState(false);

useEffect(() => {
  // 数据加载完成且需要检查时执行逻辑
  if (!isLoading && shouldCheckUsername && users) {
    const existUser = users.find((user: User) => user.username === inputValue.toLowerCase());
    
    if (existUser) {
      dispatch(userLoginActions.setLoginStatus(false));
      dispatch(userLoginActions.setErrorMessage("This username already exists!"));
    } else {
      dispatch(userLoginActions.checkUserName(inputValue));
    }
    
    // 重置标记,避免重复触发
    setShouldCheckUsername(false);
  }
}, [users, isLoading, shouldCheckUsername, inputValue, dispatch]);

const handleGuestLogin = () => {
  // 触发数据获取,并标记需要检查用户名
  dispatch(fetchUsers());
  setShouldCheckUsername(true);
};

内容的提问来源于stack exchange,提问作者seniorlearner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 10:24:13