I'm working with redux and I am trying to fetch Star War API.
Here is my code:
import { MOVIES_ERROR, MOVIE_CHARACTERS } from "./types";
// Get all characters
export const getCharacters = (userId) => async (dispatch) => {
try {
const res = await fetch(`https://swapi.dev/api/films/${userId}`);
if (!res.ok) {
throw new Error("sometheing went wrong");
}
const getData = await res.json();
const characters = await getData.characters;
let people = [];
Promise.all(
characters.map((url) =>
fetch(url)
.then((response) => response.json())
.then((name) => people.push(name))
)
);
dispatch({
type: MOVIE_CHARACTERS,
payload: people,
});
} catch (err) {
dispatch({
type: MOVIES_ERROR,
payload: { msg: err.response.statusText, status: err.response.status },
});
}
};
when I make a console log inside a promise. all I got people array filled with all the data, but when I dispatch it I got an empty array in the reducer. can anyone tell me what the mistake that i did?
I got the problem just now, I need to add await before Promise.all :)
Related
i have 2 dispatch inside the useEffect, is there a way i can make the bottom dispatch to wait for the first one to run. i need to get the post._id from the first to pass to the second. please doesn't anyone have an idea on how to solve this?
useEffect(() => {
dispatch(getPost(params.slug));
dispatch(listComment(post._id));
}, [dispatch]);
// getPost action
export const getPost = (slug) => async(dispatch) => {
try {
dispatch({
type: types.POST_REQUEST
});
const {
data
} = await axios.get(`http://localhost:8080/api/posts/${slug}`);
dispatch({
type: types.POST_SUCCESS,
payload: data,
});
} catch (err) {
dispatch({
type: types.POST_FAIL,
payload: err.response && err.response.data.message ?
err.response.data.message :
err.message,
});
}
};
You can introduce a loading state for Post, and use that in another useEffect to achieve that
useEffect(() => {
dispatch(getPost(params.slug));
}, [dispatch]);
useEffect(() => {
if(!post.loading) {
dispatch(listComment(post._id));
}
}, [dispatch, post.loading]);
It's possible to coordinate this by observing loading flags with useEffect, but a simpler solution in my opinion is to extend the thunk to dispatch another action after the response for the post is available:
// actions:
export const listComments = (postId) => async (dispatch) => {
/* thunk that fetches comments on a post */
}
// Fetch post for slug, optionally with comments.
export const getPost = (slug, withComments = false) => async(dispatch) => {
dispatch({ type: types.POST_REQUEST });
try {
const { data } = await axios.get(`http://localhost:8080/api/posts/${slug}`);
dispatch({ type: types.POST_SUCCESS, payload: data });
if (withComments) {
dispatch(listComments(data.id));
}
} catch (err) {
const payload = err.response && err.response.data.message ?
err.response.data.message : err.message;
dispatch({ type: types.POST_FAIL, payload });
}
};
// In the component:
dispatch(getPost(slug, true));
you can try with below snippet, also will this post.id get after first dipatch or it will be present?
useEffect(() => {
dispatch(getPost(params.slug));
}, [required dispaencies]);
useEffect(() => {
if(!post._id || some loading){
dispatch(listComment(post._id));
}
},[required depencies])
I have here a problem with multiple images. My problem is that I want to upload multiples image through an API. The API can't handle it through one request. That's why I wanted to upload images one by one and call the API one by one. How do i do it?
export const saveImages =
({ images = [], oldImages, isNew }) =>
async (dispatch) => {
try {
dispatch({
type: constants.REQUEST,
});
let responses;
if (images?.length) {
let formData = new FormData();
const requests = images.map(({ imageFileName, imageFile }) => {
formData.append(imageFileName, imageFile);
formData.append("isNewProduct", isNew);
return axios.post(
`${URL}/saveImages`,
formData
);
});
responses = await Promise.all(requests);
}
dispatch({
type: constants.SUCCESS,
payload: [...(oldImages || []), ...response?.data],
});
} catch (error) {
dispatch({
type: constants.FAILURE,
});
}
};
If you really need to split them up into individual requests then map the images to an array of Promises (axios.post) and await Promise.all(....) them. When all mapped promises resolve then Promise.all resolves.
export const uploadMultipleImages = ({ images }) => async (dispatch) => {
try {
dispatch({ type: constants.REQUEST });
// Map images to array of axios.post Promises
const requests = images.map(({ imageFileName, imageFile })=> {
const formData = new FormData();
formData.append(imageFileName, imageFile);
return axios.post(`${URL}/upload-image`, formData);
});
// await all promises to resolve, responses is an array of all
// resolved Promise values, i.e. whatever the POST requests
// return
const responses = await Promise.all(requests);
dispatch({
type: constants.SUCCESS,
payload: responses,
});
} catch (error) {
dispatch({ type: constants.FAILURE });
}
};
If I recall correctly on previous questions sometimes the images would have missing properties, so if this is still the case then you may want to use a .filter function before mapping to POST requests.
I am trying to filter products whether it is available or not.
Not sure how to pass an axios request with ">" logic.
I've started to create an Action
export const listProductAvailable = () => async (dispatch) => {
dispatch({
type: PRODUCT_AVAILABLE_LIST_REQUEST,
});
try {
const { data } = await Axios.get(`/api/products?countInStock>0`);
dispatch({ type: PRODUCT_AVAILABLE_LIST_SUCCESS, payload: data });
} catch (error) {
dispatch({ type: PRODUCT_AVAILABLE_LIST_FAIL, payload: error.message });
}
};
But I don't think that such a request is possible.
const { data } = await Axios.get(/api/products?countInStock>0);
Also, I don't see myself changing my Product Model creating an isAvailable boolean field as it would be redundant with countInStock =0 or not.
Hello Everyone,
need your help today with filtering in Redux...
I have filter (you can see on a picture) with several inputs (filters) for my search.
In order to receive Fleet information (filter) I need to pass an "ID" form Client...
const clientChange = (event) => {
const client = event.target.value;
const client_id = client.id;
dispatch({ type: "CLIENT_LIST_SELECTION", payload: client_id });
getFleetFilter();
};
That's my function clientChange in Form.js
const client_id = useSelector((state) => state.client.selection);
const getFleetFilter = async () => {
dispatch({ type: "FLEET_LIST_REQUEST" });
try {
let res = await getFleet(token, client_id);
let data = res.data.data
dispatch({ type: "FLEET_LIST_LOAD", payload: data });
} catch (err) {
if (err) {
console.log("Error Fleet Data");
console.log(err);
};
};
};
That's my function getFleetFilter in Search.js
Problem: I have undefined client_id in API string, because, function getFleetFilter getting called quicker, than client_id getting stored in Redux!
Question: How can I avoid this keeping using Redux here ?
Thank you!
You can use the useEffect callback for this like so:
const client_id = useSelector((state) => state.client.selection);
useEffect(() => {
if (client_id) {
getFleetFilter();
}
}, [client_id]);
const clientChange = (event) => {
const client = event.target.value;
const client_id = client.id;
dispatch({ type: "CLIENT_LIST_SELECTION", payload: client_id });
};
const getFleetFilter = async () => {
dispatch({ type: "FLEET_LIST_REQUEST" });
try {
let res = await getFleet(token, client_id);
let data = res.data.data
dispatch({ type: "FLEET_LIST_LOAD", payload: data });
} catch (err) {
if (err) {
console.log("Error Fleet Data");
console.log(err);
};
};
};
I'm trying my first react application with redux, along with Thunk as middle ware. When calling the action from one of the components, the action is hit but the code inside the action return is not executed. I have no clue what I'm missing here. I'm also using Firestore to get the data.
export const getBikeTypes = () => {
console.log('outside return')
return (dispatch, getState, { getFireBase, getFireStore }) => {
console.log('inside return')
const firestore = getFireStore();
firestore.collection('BikeTypes').get()
.then((response) => {
console.log(response)
return response
}).then(() => {
dispatch({ type: 'GET_BIKETYPES' });
}).catch((err) => {
dispatch({ type: 'GET_BIKETYPES_FAIL', err });
})
}
};
I think you should dispatch action with the payload once you get the response.
const firestore = getFireStore();
firestore.collection('BikeTypes').get()
.then((response) => {
dispatch({ type: 'GET_BIKETYPES', payload: response })
})