2

I'm using Zustand for a React Native project, and I'm not used to this setup. I have a store with some variables and functions, and I can't get Zustand to return error values for form entries.

Here is my store:

import create from 'zustand';
import auth from '@react-native-firebase/auth';

export interface UserType {
  uid: string
  displayName?: string
  email?: string
}

export interface UserStoreType {
  user: UserType | null,
  login: (email: string, password: string) => void,
  logout: () => void,
}

const UserStore = create<UserStoreType>(set => ({
    user: null,
    login: async (email: string, password: string) => {
      auth().signInWithEmailAndPassword(email, password)
        .then(res => {
          set(state => {
            return {
              ...state,
              user: {
                uid: res.user.uid
              }
            }
          }, true)
        })
        .catch((e) => {
          console.log('error: ', e)
          return new Error(e);
      })
    },
    logout: async () => {
      set(state => {
        return {
          ...state,
          user: null
        }
      })
    },
    updateUserAuth: async (user: UserType) => {

    }
  })
)

export default UserStore;

Here is my login function:

const handleLogin = async () => {
    if (!formData.email || !formData.password) {
      if (!formData.email) setErrors(prev => ({...prev, email: 'Error with email'}));
      if (!formData.password) setErrors(prev => ({...prev, password: 'Error with password'}))
      return
    }
    try {
      login(formData.email, formData.password)
    } catch(e) {
      console.log('catch running')
      console.log('Error in Login.tsx: ', e);
    }
    console.log('end of login function')
  }

The form works, but doesn't return anything if an error happens (I can only handle it in the store -- I can't return it to the invoking function)

Joel Hager
  • 2,990
  • 3
  • 15
  • 44

1 Answers1

0

Just like you're storing your state on a successful call, you can store your error in state in your catch clause. The Zustand equivalent of dispatch doesn't return anything - it tells the reducer how to manage state.

I'm not sure how you want to put it into state, but here's an example: nesting it inside your state under the key error. Then in your component, you can check if this state exists and react accordingly. I've also cleared out the user state in the example, which you could use as well.

      .then(res => {
          set(state => {
            return {
              ...state,
              user: {
                uid: res.user.uid
              }
            }
          }, true)
        })
        .catch((error) => {
          set(state => {
            return {
              ...state,
              error,
              user: {
                uid: undefined,
              },
            }
          }, true) 
      })
Abe
  • 4,500
  • 2
  • 11
  • 25
  • 1
    So basically my global store would have to universally house every possible error permutation? If I have 3,6,10,60, 'n' different components that use different calls, I'd have to store them all inside the store as different variables? That just doesn't seem efficient or logical to me... – Joel Hager May 30 '22 at 04:26
  • It's probably better to not make async calls inside your reducers at all, but you asked about this specific case, not "what is the best way to store errors". – Abe May 30 '22 at 06:34
  • I'm storing an authenticated user object in state for reuse in components. To me, it makes sense to have the login and logout 'actions' inside as I would a context. – Joel Hager May 30 '22 at 17:28
  • It's much easier to perform the async action outside the reducer. Then you can either return the error or dispatch an action depending on the result. You've trapped your error here inside the reducer's scope, so your options are to set the error as state or to dispatch another action from the reducer, both of which I encourage you not to do. – Abe May 30 '22 at 18:40