In my fullfilled i am getting response as undefined. any one please help?
code :
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import axios from "axios";
const fetchPost = createAsyncThunk('fetch/post', async (params: string) => {
try {
const { data } = await axios.get('https://registry.npmjs.org/-/v1/search', { params: { text: params } })
data.objects.map((result: any) => {
console.log('result', result)//getting result
return result.package.name;
});
} catch (err: any) {
return err?.response;
}
})
interface RepositoriesState {
loading: boolean;
error: string | null;
data: string[];
}
const initialRepoState:RepositoriesState = {
loading: false,
error: null,
data:[]
}
const repositorySlice = createSlice({
name: 'repo-slice',
initialState: initialRepoState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchPost.pending, (state) => {
state.loading = true
})
.addCase(fetchPost.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
console.log('payload', action.payload) //undefined
})
.addCase(fetchPost.rejected, (state) => {
state.loading = false;
state.error = "error in api";
state.data = [];
})
}
})
export { fetchPost };
export default repositorySlice.reducer;
Nothing is getting returned from your function... so its undefined
I guess it should be as so
const fetchPost = createAsyncThunk('fetch/post', async (params: string) => {
try {
const { data } = await axios.get('https://registry.npmjs.org/-/v1/search', { params: { text: params } })
return data.objects.map((result: any) => {
console.log('result', result)//getting result
return result.package.name;
});
} catch (err: any) {
return err?.response;
}
})
Related
IMAGE LINK HERE. it doubles the initial return.
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit'
import axios from 'axios'
let url = 'http://localhost:3000/api/v1/posts'
const initialState = {
allPosts: [],
isLoading: false,
isError: false,
}
export const getAllPost = createAsyncThunk('allPosts/getAllPost', async (page, thunkAPI) => {
console.log(page)
try {
const { data } = await axios.get(`${url}?page=${page}`)
return data.posts
} catch (error) {
console.log(error)
}
})
const allPostSlice = createSlice({
name: 'allPosts',
initialState,
reducers: {},
extraReducers: {
[getAllPost.pending]: (state) => {
state.isLoading = true
},
[getAllPost.fulfilled]: (state, { payload }) => {
console.log(payload)
state.allPosts = [...state.allPosts, ...payload]
state.isLoading = false
},
[getAllPost.rejected]: (state) => {
state.isLoading = false
state.isError = true
},
},
})
export default allPostSlice.reducer
When I try to append ...state.allPosts, which is supposed to be the old state, it prints double the initial payload. I only need it printed once, not twice.
[getAllPost.fulfilled]: (state, { payload }) => {
console.log(payload)
state.allPosts = [...state.allPosts, ...payload]
state.isLoading = false
},
How can I print my payload only once using state.allPosts?
I'm trying to fetch a list from database and add to my state. but the action.payload is undefined on the api the result is correct.
mySlice
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import { fileFetch } from "./fileAPI";
const initialState = {
loading: "idle" | "pending" | "succeeded" | "failed",
error: false,
filesUploaded: null,
};
export const fetchFiles = createAsyncThunk("files/fetchFiles", () => {
return fileFetch();
});
export const fileSlice = createSlice({
name: "files",
initialState,
reducers: {},
extraReducers(builder) {
builder
.addCase(fetchFiles.pending, (state, action) => {
state.loading = "pending";
})
.addCase(fetchFiles.fulfilled, (state, action) => {
console.log(action);
state.loading = "succeeded";
state.filesUploaded = action.payload;
})
.addCase(fetchFiles.rejected, (state, action) => {
state.loading = "failed";
state.filesUploaded = [];
state.error = action.error.message;
});
},
});
export default fileSlice.reducer;
myAPI
const api = axios.create({
baseURL: "http://localhost:8081/file/",
headers: {
"content-type": "application/json",
},
});
export const fileFetch = async () => {
await api
.get("getAll")
.then((res) => {
console.log(res.data);
return res.data;
})
.catch((err) => {
throw new Error(err);
});
};
the console.log on the api is returning the correct data.
any idea why the payload is undefined?
thanks.
Might be because you are using both async-await and Promise methods in fileFetch function. Try updating it to this
export const fileFetch = async () => {
const response = await api.get("getAll")
return response.data
};
myApi
export const fileFetch = () => api.get("getAll")
mySlice
export const fetchFiles = createAsyncThunk(
"files/fetchFiles",
async ({ rejectWithValue }) => {
try {
const response = await fileFetch();
return response.data;
} catch (error) {
return rejectWithValue(error.response.data);
}
}
);
Also working lifecycle methods
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
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",
})
}
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 ...
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,
});
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>
);
}
Hi first post here so please go easy with me. I new to redux and wondered why im not get a rerender when my store is being updated successfully.
here is my reducer
const initialState = {
pending: false,
loadPlanning: [],
error: null
}
export const loadPlanningReducer = (state = initialState, action) => {
if (action.type === INITIALISE_LOAD_PLANNING_PENDING) {
return {
...state,
pending: true
}
}
if (action.type === INITIALISE_LOAD_PLANNING_SUCCESS) {
console.log('updating state');
return Object.assign({}, state, {
...state,
pending: false,
loadPlanning: state.loadPlanning.concat(action.loadPlanning.items)
});
}
if (action.type === INITIALISE_LOAD_PLANNING_ERROR) {
return {
...state,
pending: false,
error: action.error
}
}
return state;
}
export default loadPlanningReducer;
export const getLoadPlanning = (state) => { console.log('reducer state',state); return state.loadPlanning };
export const getLoadPlanningPending = (state) => state.pending;
export const getLoadPlanningError = (state) => state.error;
The view looks like
const mapStateToProps = (state, ownProps) => ({
error: getLoadPlanningError(state),
loadPlanning: getLoadPlanning(state),
pending: getLoadPlanningPending(state),
options: state.options.options,
option: state.options.currentOption,
oidc: state.oidc
})
const mapDispatchToProps = (dispatch) => {
return {
dispatch
};
}
const fetchLoadPlanning = async (props) => {
props.dispatch(initialiseLoadPlanning());
const httpOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${xxxxxxxx}`
}
};
fetch('/Api/Planning/Get_Data?' + "database=xxxxxx", httpOptions)
.then(res => res.json())
.then(res => {
if (res.hasError) {
throw res.error;
}
props.dispatch(initialiseLoadPlanningSuccess(res.data));
return res.data;
})
.catch(error => {
props.dispatch(initialiseLoadPlanningError(error));
});
}
const LoadPlanningList = (props) => {
useEffect(() => {
fetchLoadPlanning(props);
}, [])
useEffect(() => {
console.log('props changed',props);
},[props])
}
The console log of props changed happens on change of props.pending but not on the dispatch of props.dispatch(initialiseLoadPlanningSuccess(res.data));
console log
You help and wisdom would be most helpful, thanks in advance
I have a question on handling errors in createAsyncThunk with TypeScript.
I declared returned type and params type with generics. However I tried with handling erros typing I ended up just using 'any'.
Here's api/todosApi.ts...
import axios from 'axios';
export const todosApi = {
getTodosById
}
// https://jsonplaceholder.typicode.com/todos/5
function getTodosById(id: number) {
return instance.get(`/todos/${id}`);
}
// -- Axios
const instance = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com'
})
instance.interceptors.response.use(response => {
return response;
}, function (error) {
if (error.response.status === 404) {
return { status: error.response.status };
}
return Promise.reject(error.response);
});
function bearerAuth(token: string) {
return `Bearer ${token}`
}
Here's todosActions.ts
import { createAsyncThunk } from '#reduxjs/toolkit'
import { todosApi } from '../../api/todosApi'
export const fetchTodosById = createAsyncThunk<
{
userId: number;
id: number;
title: string;
completed: boolean;
},
{ id: number }
>('todos/getTodosbyId', async (data, { rejectWithValue }) => {
try {
const response = await (await todosApi.getTodosById(data.id)).data
return response
// typescript infer error type as 'unknown'.
} catch (error: any) {
return rejectWithValue(error.response.data)
}
})
And this is todosSlice.ts
import { createSlice } from '#reduxjs/toolkit'
import { fetchTodosById } from './todosActions'
interface todosState {
todos: {
userId: number;
id: number;
title: string;
completed: boolean;
} | null,
todosLoading: boolean;
todosError: any | null; // I end up with using any
}
const initialState: todosState = {
todos: null,
todosLoading: false,
todosError: null
}
const todosSlice = createSlice({
name: 'todos',
initialState,
reducers: {
},
extraReducers: (builder) => {
builder
.addCase(fetchTodosById.pending, (state) => {
state.todosLoading = true
state.todosError = null
})
.addCase(fetchTodosById.fulfilled, (state, action) => {
state.todosLoading = false
state.todos = action.payload
})
.addCase(fetchTodosById.rejected, (state, action) => {
state.todosLoading = false
state.todosError = action.error
})
}
})
export default todosSlice.reducer;
In addition, it seems my code doesn't catch 4xx errors. Is it becasue I didn't throw an error in getTodosById in todosApi?
I don't have much experience with TypeScript so please bear with my ignorance.
UPDATE: I managed to handle errors not using 'any' type, but I don't know if I'm doing it right.
//todosActions..
export const fetchTodosById = createAsyncThunk<
{
userId: number;
id: number;
title: string;
completed: boolean;
},
number
>('todos/getTodosbyId', async (id, { rejectWithValue }) => {
const response = await todosApi.getTodosById(id);
if (response.status !== 200) {
return rejectWithValue(response)
}
return response.data
})
// initialState...
todosError: SerializedError | null;
This is described in the Usage with TypeScript documentation page:
const fetchUserById = createAsyncThunk<
// Return type of the payload creator
MyData,
// First argument to the payload creator
number,
{
// Optional fields for defining thunkApi field types
rejectValue: YourAxiosErrorType
}
>('users/fetchById', async (userId, thunkApi) => {
// ...
})