react toastify with redux from axios API

3.9k Views Asked by At

i am trying to send the error messages that sent from my server ( express ) to axios and the error message displays in toastify component but the error message doesn't show up here is the login axios function with the toastify how can i display toastify message inside my page from redux ?

here is my code :

// redux controller
const login = async (username, password) => {  
await axios.post("/login",{username,password,},
        { withCredentials: true });};

 // reducer page
export function generateError(prop) {
    return function (dispatch) {
        dispatch({
            type: "USER_FAIL"
        });
        toast.error(prop);
    };
}

export function generateSuccess(prop) {
    return function (dispatch) {
        dispatch({
            type: "USER_SUCCESS"
        });
        toast.success(prop);
    };
}

export const login = createAsyncThunk(
"/login",
async ({ username, password }) => {
    try {
        const data = await authService.login(username, password);
        if (data) {
            if (data.errors) {
                const { username, password } = data.errors;
                if (username) generateError(username)
                else if (password) generateError(password);
            } else {
                generateSuccess(data.success);
            }
        }
        return { user: data };
    } catch (error) {
        console.log(error);
    }
}
  );

// login page

 const handleSubmit = (e) => {
    e.preventDefault();
    dispatch(login({ username, password }));
}

i am using react-tostify and @redux-toolkit but the message doesn't display inside my page

4

There are 4 best solutions below

1
On

i fixed it and here is my code :

// auth.js ( redux page )

export const login = createAsyncThunk(
"/login",
async ({ username, password }) => {
    try {
        const {data} = await axios.post(
            "/login",
            {
                username,
                password,
            },
            { withCredentials: true }
        ); 
        return { user: data };
    } catch (error) {
        console.log(error);
    }
});


const initialState = user
? { isLoggedIn: true, user }
: { isLoggedIn: false, user: null };

const authSlice = createSlice({
name: "auth",
initialState,
extraReducers: {
    [login.fulfilled]: (state, action) => {
        state.isLoggedIn = true;
        state.user = action.payload.user;
    },
    [login.rejected]: (state, action) => {
        state.isLoggedIn = false;
        state.user = null;
    },
    [logout.fulfilled]: (state, action) => {
        state.isLoggedIn = false;
        state.user = null;
    },
}})
const { reducer } = authSlice; export default reducer;

Login Page :

const { isLoggedIn } = useSelector((state) => state.auth);
const dispatch = useDispatch();

const handleSubmit = (e) => {
    e.preventDefault();
    dispatch(login({ username, password })).then(data => {
        console.log(data)
        if (data.payload.user) {
            if (data.payload.user.errors) {
                const { username, password } = data.payload.user.errors;
                if (username) generateError(username)
                else if (password) generateError(password);
            } else {
                generateSuccess(data.success);
                navigate("/dashboard");
            }
        }
    })
}

i realized when i back the data it has an object name payload i used it to get the error messages from express and then i put the message in toastify function gettingError and here it is

const generateError = error => {
    toast.error(error, {
        position: "bottom-right",
    })
}
0
On

First createAsyncThunk:

import { coreAxios } from "utilities/axios"; // Own customized axios
import { createAsyncThunk } from "@reduxjs/toolkit";

const BASE_URL = process.env.REACT_APP_MAIN_URL

export const GetProducts = createAsyncThunk(
  "inventory/GetProducts",
  async () => {
    const {data} = await coreAxios.get(`${BASE_URL}/api/product/list/`);
    return data
  }
);

Second createSlice:

import { createSlice } from "@reduxjs/toolkit";
import { GetProducts } from "services/inventory/product.service";
import { toast } from 'react-toastify';

export const productSlice = createSlice({
  name: "products",
  initialState: {
    productsList: [],
    productsLoading: false,
    productsError: null,
  },
  extraReducers: 
  (builder) => {
    builder.addCase(GetProducts.pending, (state) => {
        toast.loading('Promise is pending...')
        state.productsLoading = true
    });

    builder.addCase(GetProducts.fulfilled, (state, action) => {
      toast.dismiss();
      toast.success('Promise resolved ');

      state.productsList = action.payload
      state.productsLoading = false
      state.productsError = null
    });

    builder.addCase(GetProducts.rejected, (state, action) => {
      toast.dismiss();
      toast.error('Promise rejected  ')

      state.productsLoading = false
      state.productsError = action.error?.message
    });
  },
});

export default productSlice.reducer;

Third page:

import { ToastContainer } from 'react-toastify';
import { useSelector, useDispatch } from "react-redux";
import { GetProducts } from 'services/inventory/product.service';

const Product = () => {
    const { productsList, productsLoading, productsError } = useSelector((state) => state.products);
    const dispatch = useDispatch();

    useEffect(() => {
        dispatch(GetProducts());
    }, []);

    return (
        <div className="grid crud-demo">
            <h1>Hello Alim</h1>
            <ToastContainer />
        </div>
    );

}

0
On

Hai I'm also looking for the same problem while searching I found a solution at with this : react-toastify-with-redux

my Code : authAction.js

import 'react-toastify/dist/ReactToastify.min.css';
import { toast} from 'react-toastify';

export const registerUser = (userData) => dispatch =>{
axios.post('user/register',userData)
.then(res=>toast.success('Your Account Created Successfully '))
.then(res=> window.location = '/authentication/sign-in')
.catch(err=>dispatch(
    {
        type: GET_ERRORS,
        payload: err.response.data
    }
),toast.error("Error "))
// .catch((err)=> {return })

};

On your signUp page just add

<ToastContainer />

That's all ...

0
On

This answer is probably late. But I came across this problem and decided to do it my way. I know there is toast. promise to handle promises and I don't want to call dispatch.then every time. So I can up with passing dispatch to my action wrapper. Here is my code.

// utils.ts
type ArgumentTypes<F extends CallableFunction> = F extends (
  ...args: infer A
) => any
  ? A[0]
  : never;

export const withToast = <T = AnyAction | typeof createAsyncThunk>(
  action: T,
  { pending, error, success }: ToastPromiseParams<T>
) => {
  return (
    dispatch: ReturnType<typeof useAppDispatch>,
    actionParams?: ArgumentTypes<T & CallableFunction> | void
  ) => {
    const promise = dispatch(
      (action as CallableFunction)(actionParams as any)
    ).unwrap();
    toast.promise(promise, {
      pending,
      error,
      success,
    });
  };
};
// actions.ts
export const login = createAsyncThunk(
  "user/login",
  async (payload: {
    email: string;
    password: string;
  }): Promise<Partial<LoginAPIResponse>> => {
    const { data } = await axios.post(`${API}/${LOGIN_EP}/`, payload);
    return data;
  }
);

export const loginWithToast = withToast(login, {
  pending: "Logging in...",
  error: {
    render: (error: any) => {
      return error?.password || error?.email
        ? "Invalid email or password"
        : "Something went wrong";
    },
  },
  success: "Logged in successfully",
});
// usage in component
  const dispatch = useAppDispatch();
  loginWithToast(dispatch, {
        email: values.email.value,
        password: values.password.value,
      });