Unable to pass object in createAsyncThunk in Redux Toolkit - javascript

I am trying to pass two parameters in createProductReview action when submitHandler function runs.
One is id and other one is an object containing two items. The problem is that I am unable to pass the object in createProductReview action. It gives undefine when I console log it in reducer function. I want to know how can I pass these two arguments without getting error.
Please check out attached image for error
submitHandler function
const submitHandler = (e) => {
e.preventDefault();
dispatch(createProductReview({ id, { rating, comment } }));
};
createProductReview
export const createProductReview = createAsyncThunk(
'reviewProduct',
async ({ productId, review }, thunkAPI) => {
console.log(productId, review);
try {
const {
userLogin: { userInfo },
} = thunkAPI.getState();
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userInfo.token}`,
},
};
await axios.post(`/api/products/${productId}/reviews`, review, config);
} catch (error) {
const newError =
error.response && error.response.data.message
? error.response.data.message
: error.message;
return thunkAPI.rejectWithValue(newError);
}
}
);

In javascript, you need to pass keys to the object so it should be like this
dispatch(createProductReview({ productId:id, review:{ rating, comment } }));
Specially, when you are destructuring it in the function. Since destructure works by getting the object with its key.
so for example:
const x = {temp:"1"}
const {temp} = x;
console.log(temp);
//1

Related

How to automatically recover the data that has just been created?

I explain my problem:
I have two functions, one to create a garden and another a plot. So it's POST requests to my API but when I click submit, I have another function (which is a GET request) that retrieves the garden and the plots, this function works but I have to reload the page for them to appear: it's not added automatically. How can I do this?
I tried putting in the array of useEffect (which repeats my function to retrieve data from the Garden and the plots) the variable garden and plot or setGarden and setPlot but it doesn't work.
Here is the code of the GET Request :
const [garden, setGarden] = useState([]);
const [plot, setPlot] = useState([]);
const [loading, setLoading] = useState(false);
const gardenData = async () => {
setLoading(true);
const user = await AsyncStorage.getItem('user');
const parsedUserData = JSON.parse(user);
try {
const response = await axios.get(
`http://127.0.0.1/api/garden?user=${parsedUserData.user.id}`,
{
headers: {
Authorization: `Token ${parsedUserData.token}`,
},
},
);
if (response.status === 200) {
setGarden(response.data);
setLoading(false);
try {
const plotResponse = await axios.get(
`http://127.0.0.1/api/plots?garden=${response.data[0].id}`,
{
headers: {
Authorization: `Token ${parsedUserData.token}`,
},
},
);
if (plotResponse.status === 200) {
setPlot(plotResponse.data);
}
} catch (e) {
alert(e);
}
}
} catch (e) {
console.log('Erreur ' + e);
setLoading(false);
}
};
useEffect(() => {
gardenData();
}, []);
Thanks for the help !
I am assuming that the mentioned code is a part of an existing component with a return. It would be great to see where you run the POST method.
The useEffect hook gets triggered only on the mount of the component (as defined with the second argument - [])
So you need to call the gardenData function after successfully performing the POST request. After that, the data will refresh and state will take care of the rest.

React SWR - how to know that updating (mutating) is running?

Im mostly using SWR to get data, however I have a situation that I need to update data. The problem is, I need an indicator that this request is ongoing, something like isLoading flag. In the docs there's a suggestion to use
const isLoading = !data && !error;
But of course when updating (mutating) the data still exists so this flag is always false. The same with isValidating flag:
const { isValidating } = useSWR(...);
This flag does NOT change when mutation is ongoing but only when its done and GET request has started.
Question
Is there a way to know if my PUT is loading? Note: I dont want to use any fields in state because it won't be shared just like SWR data is. Maybe Im doing something wrong with my SWR code?
const fetcher = (url, payload) => axios.post(url, payload).then((res) => res);
// ^^^^^ its POST but it only fetches data
const updater = (url, payload) => axios.put(url, payload).then((res) => res);
// ^^^^^ this one UPDATES the data
const useHook = () => {
const { data, error, mutate, isValidating } = useSWR([getURL, payload], fetcher);
const { mutate: update } = useSWRConfig();
const updateData = () => {
update(getURL, updater(putURL, payload)); // update data
mutate(); // refetch data after update
};
return {
data,
updateData,
isValidating, // true only when fetching data
isLoading: !data && !error, // true only when fetching data
}
Edit: for any other who reading this and facing the same issue... didnt find any solution for it so switched to react-query. Bye SWR
const { mutate: update } = useSWRConfig();
const updateData = () => {
// this will return promise
update(getURL, updater(putURL, payload)); // update data
mutate(); // refetch data after update
};
By using react-toastify npm module to show the user status.
// first wrap your app with: import { ToastContainer } from "react-toastify";
import { toast } from "react-toastify";
const promise=update(getURL, updater(putURL, payload))
await toast.promise(promise, {
pending: "Mutating data",
success: "muttation is successfull",
error: "Mutation failed",
});
const markSourceMiddleware = (useSWRNext) => (key, fetcher, config) => {
const nextFetcher = (...params) =>
fetcher(...params).then((response) => ({
source: "query",
response,
}));
const swr = useSWRNext(key, nextFetcher, config);
return swr;
};
const useHook = () => {
const {
data: { source, response },
mutate,
} = useSWR(key, fetcher, { use: [markSourceMiddleware] });
const update = mutate(
updateRequest().then((res) => ({
source: "update",
response,
})),
{
optimisticData: {
source: "update",
response,
},
}
);
return {
update,
updating: source === "update",
};
};
Hmm based on that:
https://swr.vercel.app/docs/conditional-fetching
It should work that the "is loading" state is when your updater is evaluates to "falsy" value.
REMAINDER! I don't know react swr just looked into docs - to much time at the end of the weekend :D
At least I hope I'll start discussion :D

ReactJS - response in async call

I'm learning axios and async calls, please sorry if this is too basic. I have this axios call:
trackComponent.jsx
getTrack(event) {
const {select, userId} = this.props
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/track/${select}/${userId}/${this.props.spotifyToken}`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`
}
};
return axios(options)
.then((res) => {
this.setState({
playlist: res.data.data[0].playlist,
artists: res.data.data[0].artists,
previews: res.data.data[0].previews,
youtube_urls: res.data.data[0].youtube_urls,
})
})
.catch((error) => { console.log(error); });
};
Now, I'm refactoring my code, and I've implemented an apiService in front of all my component calls in order to deal with authorization, like so:
trackComponent.jsx
import {apiService} from '../ApiService'
async track(event) {
if (this.props.isAuthenticated) {
const {userId, spotifyToken} = this.props;
const {artist} = this.state
const tracks = await apiService.getTrack(userId, spotifyToken, artist) ;
this.setState({tracks});
} else {
this.setState({tracks: []});
}
}
an in ApiService.js I have:
async getTrack(userId, spotifyToken, select) {
return this.axios.get(
`${process.env.REACT_APP_WEB_SERVICE_URL}/track/${artist}/${userId}/${spotifyToken}`
);
}
Now how do I tweak this new async track(event) in the component in order to keep my 'response' and set the following states,
playlist: res.data.data[0].playlist,
artists: res.data.data[0].artists,
previews: res.data.data[0].previews,
youtube_urls: res.data.data[0].youtube_urls,
which were being passed as response inside then() of the first getTrack(event)?
Provided the API call executes successfully, tracks (the value you're returning from getTrack) will contain the responses you're looking for.
If you console.log it, you'll see the various fields. At that point, it's just a matter of accessing the values and setting state with them:
const tracks = await apiService.getTrack(userId, spotifyToken, artist);
const firstEntry = tracks.data.data[0];
this.setState({
playlist: firstEntry.playlist,
artists: firstEntry.artists,
...
});

Why is my axios post returning undefined in my functional component?

I'm new to Next Js and functional comoponents. I'm trying to retrieve data from /api/retrieve2
//this is retrieve page
export default function Retrieve() {
const onSubmit = async data => {
const { user } = await axios.post("/api/retrieve2", data);
console.log(user) // user here is undefined
};
return (...);
}
//this is retrieve2, inside the API folder
export default async (req, res) => {
try {
const { data } = await axios.post(myBackendUrl, req.body);
console.log(data) //this is printing the right data - { email: 'casas#gmail.com', code: '123123' }
res.json(data);
} catch (e) {
res.json({ err: e.message || e });
}
};
What am I missing, is this something about Next? About functional components?
You should read about ES6 destructuring
You try to destructure user but the axios respons witch is a object doesnt contain the key user
For data it works because there is a data property in the response
Here are all properties that you can destructure:
{ data, status, statusText, headers, config, request }
You need to get the full URL to make http request to using getInitialProps, here Home is the name of your component
const Home = ({ENDPOINT}) => {
const onSubmit = async data => {
const { data } = await axios.post(`${ENDPOINT}/api/retrieve2`, data);
// consider changing `user` here to `data` since Axios stores response in data object
console.log(data) // should be defined
};
return (...);
}
Home.getInitialProps = ctx => {
const ENDPOINT = getEndpoint(ctx.req);
return { ENDPOINT };
};
// You should store this somewhere you can reuse it
export function getEndpoint(req) {
return !!req
? `${req.headers['x-forwarded-proto']}://${req.headers['x-forwarded-host']}`
: window.location.origin;
}

Get specific value from array of objects [{"key":"value"}, {"key":"value"}]

I'm trying to interact with the API of processmaker.
I have made a simple form to authenticate and get the authorization token, which is needed to interact with the rest of the API.
I am able to use the token to output a json response of created projects after login. The response is an array of objects.
I need to get the prj_uid for an api request so I want to extract these, but I've had little luck using map.
How can I iterate over the response and get prj_name and prj_uid for each of the objects in the array?
import React, { useState, useEffect } from "react";
//import ResponsiveEmbed from "react-responsive-embed";
const Tasks = ({ loggedIn }) => {
const [hasError, setErrors] = useState(false);
const [projects, setProjects] = useState([]);
const url = "http://127.0.0.1:8080/api/1.0/workflow/project";
useEffect(() => {
let access_token = sessionStorage.getItem("access_token");
async function fetchData() {
const response = await fetch(url, {
method: "GET",
withCredentials: true,
timeout: 1000,
headers: {
Authorization: "Bearer " + access_token
}
});
response
.json()
.then(response => setProjects(response))
.catch(err => setErrors(err));
}
fetchData();
}, [loggedIn]);
console.log(JSON.stringify(loggedIn) + " logged in, displaying projects");
console.log(projects + " projects");
if (!loggedIn) {
return <h1>Error</h1>;
} else {
return (
<>
<p>Login success!</p>
<h2>Projects:</h2>
<span>{JSON.stringify(projects)}</span>
<div>Has error: {JSON.stringify(hasError)}</div>
</>
);
}
};
export default Tasks;
Stringified Response:
[
{
"prj_uid":"1755373775d5279d1a10f40013775485",
"prj_name":"BPMN Process",
"prj_description":"This is a processmaker BPMN Project",
"prj_category":"8084532045d5161470c0de9018488984",
"prj_type":"bpmn",
"prj_create_date":"2019-08-13 08:50:25",
"prj_update_date":"2019-08-13 09:04:16",
"prj_status":"ACTIVE"
},
{
"prj_uid":"7459038845d529f685d84d5067570882",
"prj_name":"Purchase Request",
"prj_description":"",
"prj_category":"2284311685392d2e70f52e7010691725",
"prj_type":"bpmn",
"prj_create_date":"2019-08-13 11:30:48",
"prj_update_date":"2019-08-13 12:20:05",
"prj_status":"ACTIVE"
}
]
Array.map() is your answer- you had it right.
its as simple as:
let mappedObject = result.map( el => ({ prj_name, prj_uid }) );
el is every element in the array, and we construct the new array with an object containing only prj_name and prj_uid. Because el alraeady has those properties with those names, we do not need to write { prj_name: el.prj_name } when we construct the new object, it is implied and will do the trick with only the property names there.
mappedObject will now hold an array of objects consists only of the asked properties.
You might wanna read more about map to understand it better- Array.map()
If loggedIn is the json object, then you can do this:
const uidNameArr = loggedIn.map((item) => { // returns an array of arrays with the values you want.
return [item.prj_uid, item.prj_name]
})
uidNameArr.forEach(([uid,name]) => {
console.log(`${name} has a uid of ${uid}`)
})

Categories