Search code examples
javascriptreactjsredux-toolkit

Redux: Actions must be plain objects error - How to resolve?


I'm encountering an error in my React Native application when trying to save user data and login details to Firebase using Redux. The error message I'm receiving is:

[Error: Actions must be plain objects. Instead, the actual type was: 'undefined'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions.

I'm using Redux to manage my application's state, and I've configured a store using @reduxjs/toolkit. The strange thing is that the data is being saved successfully to Firebase, but I'm still getting this error.

Here's a simplified version of my Redux setup:

Signup.js

const isTestMode = true;
const initialState = {
  inputValues: {
    username: isTestMode ? "John Doe" : "",
    email: isTestMode ? "email@gmail.com" : "",
    password: isTestMode ? "12121212" : "",
  },
  inputValidities: {
    username: false,
    email: false,
    password: false,
  },
  formIsValid: false,
};

export default function Signup({ navigation }) {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const [formState, dispatchFormState] = useReducer(reducer, initialState);
  const dispatch = useDispatch();

  const inputChangedHandler = useCallback(
    (inputId, inputValue) => {
      const result = validateInput(inputId, inputValue);
      dispatchFormState({ inputId, validationResult: result, inputValue });
    },
    [dispatchFormState]
  );

  const signupHandler = async () => {
    try {
      setIsLoading(true);
      const action = signUp(
        formState.inputValues.username,
        formState.inputValues.email,
        formState.inputValues.password
      );
      await dispatch(action);
      Alert.alert("Account Successfully created", "Account created");
      setError(null);
      setIsLoading(false);
      navigation.navigate("Login");
    } catch (error) {
      console.log(error);
      setIsLoading(false);
      setError(error.message);
    }
  };

 
  return (
    <SafeAreaProvider>
      <View style={styles.container}>
        <View style={styles.inputView}>
          <Inputs
            id="username"
            placeholder="Username"
            errorText={formState.inputValidities["username"]}
            onInputChanged={inputChangedHandler}
          />
          <Inputs
            id="email"
            placeholder="Enter your email"
            errorText={formState.inputValidities["email"]}
            onInputChanged={inputChangedHandler}
          />
          <InputsPassword
            id="password"
            placeholder="Password"
            errorText={formState.inputValidities["password"]}
            onInputChanged={inputChangedHandler}
          />
        </View>

        <Buttons
          title="SIGN UP"
          onPress={signupHandler}
          isLoading={isLoading}
        />

        <StatusBar style="auto" />
      </View>
    </SafeAreaProvider>
  );
}

AuthSlice.js

import { createSlice } from "@reduxjs/toolkit";

const authSlice = createSlice({
  name: "auth",
  initialState: {
    token: null,
    userData: null,
    didTryAutoLogin: false,
  },
  reducers: {
    authenticate: (state, action) => {
      const { payload } = action;
      state.token = payload.token;
      state.userData = payload.userData;
      state.didTryAutoLogin = true;
    },
    setDidTryAutoLogin: (state, action) => {
      state.didTryAutoLogin = true;
    },
  },
});

export const authenticate = authSlice.actions.authenticate;
export default authSlice.reducer;

AuthAction.js

export const signUp = (username, email, password) => {
  return async (dispatch) => {
    const app = getFirebaseApp();
    const auth = getAuth(app);
    try {
      const result = await createUserWithEmailAndPassword(
        auth,
        email,
        password
      );
      const { uid, stsTokenManager } = result.user;
      const { accessToken, expirationTime } = stsTokenManager;
      const expiryDate = new Date(expirationTime);
      const userData = await createUser(username, email, uid);
      dispatch(authenticate({ token: accessToken, userData }));
      saveToDataStorage(accessToken, uid, expiryDate);
      dispatch();
    } catch (error) {
      console.log(error);
      const errorCode = error.code;
      let msg = "Something went wrong";
      if (
        errorCode === "auth/wrong-password" ||
        errorCode === "auth/user-not-found"
      ) {
        msg = "Invalid email or password";
      }
      throw new Error(msg);
    }
  };
};

const createUser = async (username, email, userId) => {
  const userData = {
    username,
    email,
    userId,
    signUpDate: new Date().toISOString(),
  };
  const dbRef = ref(getDatabase());
  const childRef = child(dbRef, `users/${userId}`);
  await set(childRef, userData);
  return userData;
};

const saveToDataStorage = (token, userId, expiryDate) => {
  AsyncStorage.setItem(
    "userData",
    JSON.stringify({
      token,
      userId,
      expiryDate: expiryDate.toISOString(),
    })
  );
};

Store.js

import { configureStore } from "@reduxjs/toolkit";
import authSlice from "./authSlice";

export const store = configureStore({
  reducer: {
    auth: authSlice,
  },
});

Solution

  • In your AuthAction.js, you have a call to dispatch() without arguments. You have to dispatch something, or you'll provoke this error.

    If you don't want to dispatch anything there, remove that line.

         const userData = await createUser(username, email, uid);
          dispatch(authenticate({ token: accessToken, userData }));
          saveToDataStorage(accessToken, uid, expiryDate);
    // remove this line
    //      dispatch();
        } catch (error) {