Redux Saga 不等待 Firebase 服务 return

Redux Saga not waiting for Firebase service to return

SignUpSaga 已损坏。它不会等待“yield put(updateUserAction(user));”到 return 一个值,它只是 returns undefined 破坏应用程序然后,在 returns 之后注册服务.

如何让它等待?我以为这就是屈服的作用。

部分 redux 包:

export const updateUserAction = (user: User | null): UpdateUserActionType => ({
  type: UPDATE_USER,
  user,
});

...

export const userReducer = (
  state: StateSlice = initialState.user,
  action: UpdateUserActionType
): StateSlice => {
  switch (action.type) {
    case UPDATE_USER:
      return updateHandler(state, action);
    case SIGN_OUT:
      return signOutHandler();
    default:
      return state;
  }
};

互动者

export class SignUpInteractor {
  signUpService: SignUpService;

  constructor(signUpService: SignUpService) {
    this.signUpService = signUpService;
  }

  async signUp(
    firstName: string,
    lastName: string,
    credential: Credential
  ): Promise<User> {
    const user = new User(firstName, lastName, credential.email);
    return this.signUpService.signUpUser(user, credential);
  }

}

传奇

function* signUpSaga(action: SignUpActionType) {
  const { firstName, lastName, credential } = action;
  const service = new FirebaseLogin();
  const interactor = new SignUpInteractor(service);
  const user = yield call(() => {
    interactor.signUp(firstName, lastName, credential);
  });
  
  yield put(updateUserAction(user));
}

最后,注册服务:

export class FirebaseLogin implements SignUpService {
  async signUpUser(user: User, credential: Credential): Promise<User> {
    var user: User;

    try {
      db.app
        .auth()
        .createUserWithEmailAndPassword(credential.email, credential.password)
        .then((authData) => {
          db.app
            .database()
            .ref("users/" + authData.user.uid)
            .set({
              uid: authData.user.uid,
              email: credential.email,
              firstName: user.firstName,
              lastName: user.lastName,
            })
            .then(() => {
              db.app
                .database()
                .ref("users/" + authData.user.uid)
                .once("value")
                //do this to make sure data was returned as values, not resolved later by firebase.
                .then((snapchat) => {})
                .then(() => {
                  console.log("returning");
                  return user;
                });
            })
            .catch((error) => {
              return new User("error", "error", "err@err.com");
            });
        })
        .catch((error) => {
          console.log("error here");
          return new User("error", "error", "err@err.com");
        });
    } catch (error) {
      console.log("error here");
      return new User("error", "error", "err@err.com");
    }
  }

我认为主要问题是您如何使用 call:

  const user = yield call(() => {
    interactor.signUp(firstName, lastName, credential);
  });

您正在传递一个同步函数,该函数将立即启动异步调用和 return。此外,该函数 return 什么都没有,因此 undefined.

您真正想要的是告诉中间件进行异步调用,从而等待 promise 被解析:

const user = yield call(interactor.signUp, firstName, lastName, credential);

此外,您的代码看起来 wayyyy 比需要的更复杂:手动编写 Redux 代码,reducer 中的额外函数,为操作定义单独的 TS 类型对象,还有这个“交互者”和“服务”位。在此示例中,即使使用 sagas 也太过分了。您应该使用 our official Redux Toolkit package 并遵循我们关于将 Redux 与 TS 结合使用的建议。这将大大简化您的代码。

如果我自己写这个,我会让 signUp 部分成为一个独立的异步函数,并使用 RTK 的 createAsyncThunk:

async function signUpUser(user: User, credential: Credential): Promise<User> {
  // omit Firebase code
  return user;
}

interface SignUpArgs {
  firstName: string;
  lastName: string;
  credential: Credential;
}

const signUp = createAsyncThunk(
  'users/signUp',
  async ({firstName, lastName, credential}: SignUpArgs) => {
    const user = new User(firstName, lastName, credential.email);
    return signUpUser(user, credential);
  }
)

type UserState = User | null;
const initialState : UserState = null;

const userSlice = createSlice({
  name: 'users',
  initialState, // whatever your state actually is,
  reducers: {
    // assorted reducer logic here
    signOut(state, action) {
      return null;
    }
  },
  extraReducers: builder => {
    builder.addCase(signUp.fulfilled, (state, action) => {
      return action.payload
    });

  }
})

最终代码少了很多,尤其是 如果您在 reducer 中进行任何实际有意义的状态更新。