send multiple RTK query request in response of another RTK Query - reactjs

I am new to RTK Query and when I fetch some data from an endpoint I get a response of an array of objects for each id of item in the list I have to call another API to get the details of each item.
but I do not know have to achieve this.
for example:
query: () => '/posts'; // response is ==> [{id: 21, title:'Hello world'}]
and the for the details of the post with an id of 21
query: (id) => `post/${id}/detail`; // response { description:'', img:'', ... }
I need to show all posts with details. and for that, I have to get all the details on the list first and then return the result from query to later show it on the page.

i came up with this so far
import { createApi } from '#reduxjs/toolkit/query/react';
import axiosBaseQuery from 'api/axiosBaseQuery';
import axios from 'services/request';
export const postsApi = createApi({
reducerPath: 'api/portfolio',
baseQuery: axiosBaseQuery(),
endpoints: (builder) => ({
getUserPosts: builder.query({
async queryFn() {
// get a random user
const postsList = await axios.get('/posts');
if (postsList.error) return { error: postsList.error };
// const result = await axios.get(`/market/instruments/${item.refId}/summary`);
const endpoints = postsList.data.map((item) =>
axios.get(`/post/${item.id}/details`)
);
let error = null;
let data = null;
try {
data = await Promise.all(endpoints).then((res) => {
return res.map((item, index) => {
return { ...item.data, ...postsList.data[index] };
});
});
} catch (err) {
error = err;
}
return data ? { data } : { error };
}
})
})
});
export const { useGetUserPostsQuery } = postsApi;

Related

React RTK query Mutation can return value?

Is is possible to get the response of endpoint in React RTK Query Mutation .
I have a one mutation which insert into DB and I need to get the ID which inserted. in my api :
addRecord: build.mutation({
query(data) {
return {
url: base + 'myControler/SaveDataAsync',
method: 'post',
data: data,
}
},
}),
and in my component after import my hook I call it like
const [addRecord] = useAddRecordMutation();
and then in my submit function is use it like
const handleSubmitCustom = async (values) => {
await addRecord(values);
}
which I need the return value of await addRecord(values);
You can just do
const handleSubmitCustom = async (values) => {
try {
const returned = await addRecord(values).unwrap();
} catch (error) {
// you can handle errors here if you want to
}
}

Returning real time data from firebase using onSnapshot and rtk query

I am trying to return real time data from firebase using onSnapshot rtk query but it is not working
rtkquery code (apiSlice.js)
export const apiSlice = createApi({
reducerPath:'api/apiSlice',
baseQuery: fakeBaseQuery(),
endpoints: builder => ({
getPosts: builder.query<Posts[],void>({
async queryFn():Promise<any>{
try{
//const tweetsRef=collection(db,'tweets');
let tweetsArr: { }[];
onSnapshot(collection(db,'tweets'),(querySnapshot)=>{
tweetsArr=querySnapshot.docs.map((doc)=>{
console.log(doc.data());
return {...doc.data()}
})
})
return {data:tweetsArr}
}
catch(err:any){
return{error:err}
}
}})
})
})
export const { useGetPostsQuery, useAddPostMutation } = apiSlice
When returning the data I am getting an error saying 'Variable 'tweetsArr' is used before being assigned.'
Trying to access the data
const PostsList = () => {
const {data:posts,isLoading,error,isError} = useGetPostsQuery();
let content;
if(isLoading){
content=<LoadingSpinner/>
}
else if(isError){
let a:any=error
content=<p>{a?.message}</p>
}
else if(posts){
console.log(posts)
content=posts?.map((post,index) => (
<Post key={index} {...post}/>
))
}
return (
<section className="posts-list">
{content}
</section>
)
}
onSnapshot() listener can only return within the method.
Sample code below:
try{
let tweetsArr = [];
onSnapshot(collection(db,'tweets'),(querySnapshot)=>{
tweetsArr=querySnapshot.docs.map((doc)=>{
tweetsArr.push(doc.data());
console.log('correct', tweetsArr);
})
console.log('wrong', tweetsArr);
})
}catch(error){
console.log(error);
}
Output:
You can use onSnapshot() to listen to the results of a query. Listen to multiple documents in a collection you may refer to this link.
Used a promise to solve the problem of tweetArr being undefined when returning.
let tweetsArr: { }[]=[];
return new Promise((resolve, reject) => {
onSnapshot(collection(db,'tweets'),(querySnapshot)=>{
tweetsArr=querySnapshot.docs.map((doc)=>{
console.log(doc.data());
return {...doc.data()}
})
console.log(tweetsArr);
resolve({data:tweetsArr});
})})

How to fetch API data from Axios inside the getServerSideProps function in NextJS?

I'm building an App with Next.js, and I need to connect to specific API routes (set up with API Platform) and populate pages with the route's responses.
The API is working fine, but no matter how I try to implement my Axios call inside the getServerSideProps, I always get the same error, ECONNREFUSED, from my Node stack.
I tried to get the data from useEffect() and it's working fine, but I would like to know if there's a way to call it directly in getServerSideProps.
I'm using a Node container for Docker, and the routes are authenticated through a JWT Token (stored in the session and the client cookies for the server-side connection)
Here are is my code:
pages/accounts.js:
export async function getServerSideProps(context) {
const cookies = new Cookies(context.req.headers.cookie)
const adminToken = cookies.get('jwtToken')
const res = await getAllAccounts(adminToken)
return {
props: {
testdata: ''
},
}
}
lib/accounts.js:
import service from '../service'
export const getAllAccounts = async (adminToken) => {
const res = service({ jwtToken : adminToken }).get(`/accounts`).then((response) => {
}).catch((error) => {
console.dir(error)
})
}
HTTP wrapper:
import axios from 'axios';
import jwt_decode from "jwt-decode";
import mockAdapter from 'axios-mock-adapter';
const service = ({ jwtToken = null, store = null, mockURL = null, mockResponse = null, multipart = false } = {}) => {
const options = {};
options.baseURL = process.env.NEXT_PUBLIC_API_URL + '/api';
if(multipart === true) {
options.headers = {
'Content-Type': 'multipart/form-data'
}
} else {
options.headers = {
'Content-Type': 'application/ld+json',
accept: 'application/ld+json'
}
}
const instance = axios.create(options);
instance.interceptors.response.use(response => {
return response;
}, error => {
return Promise.reject(error);
})
if (mockURL !== null && mockResponse !== null) {
let mock = new mockAdapter(instance);
mock.onAny(mockURL).reply(200, mockResponse)
}
return instance;
};
export default service;
Through the error dump in the node stack, I managed to see that the request headers are correct, and the JWT correctly passed through.
Do not use Axios. Just use fetch().
Next.js polyfills fetch() by default on both the client and server, so you can just use it:
In addition to fetch() on the client-side, Next.js polyfills fetch() in the Node.js environment. You can use fetch() in your server code (such as getStaticProps/getServerSideProps) without using polyfills such as isomorphic-unfetch or node-fetch.
Source.
getServerSideProps works well with axios if we return response.data
export const getServerSideProps: GetStaticProps = async ({ params }) => {
const { brandName } = params as IParams;
const brandData = await $host.get(`api/brand/${brandName}`).then(response => response.data);
return {
props: {
brand: brandData,
},
};
};
Your problem is that your async method does not return a promise.
import service from '../service'
export const getAllAccounts = async (adminToken) => {
const res = service({ jwtToken : adminToken }).get(`/accounts`);
return res;
}
In my NextJS begining I followed this tutorial , and I changed fetch to axios in this way:
export const getStaticPaths = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await res.json();
const paths = data.map((ninja) => {
return {
params: { id: ninja.id.toString() },
};
});
return {
paths,
fallback: false,
};
};
export const getStaticProps = async (context) => {
const id = context.params.id;
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
const data = await res.json();
return {
props: { ninja: data },
};
};
I applied the change using useEffect()
useEffect(() => {
// const data = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
// const res = await data.json();
// setninja(res);
const fetchData = async () => {
const result = await axios(`https://jsonplaceholder.typicode.com/users/${id}`);
setninja(result.data);
};
fetchData();
console.log(data);
}, []);
I hope this info will be useful for you.
I Used Axios in getServerSideProps without any problems.
export const getServerSideProps: GetServerSideProps = async({
params,
res
}) => {
try {
const response = await axios.get(`/api/test`);
return {
props: {
data: response.data
},
}
} catch {
res.statusCode = 404;
return {
props: {}
};
}
};

Not able to get values from the api to chart.js, react js

so this is my api, which is stored in a url
"https://covid19.mathdro.id/api/confirmed"
this is my api index file
import axios from "axios";
const url = "https://covid19.mathdro.id/api/confirmed";
export const fetchData = async () => {
try {
const {
data: countryRegion ,
} = await axios.get(url);
return { countryRegion };
} catch (error) {
return error;
}
};
in this sandbox code, i have tried to take the value of countryRegion from the api, but it appears as undefied in the console.
https://codesandbox.io/s/react-chartjs-2-nqo9n
Looks like you are destructuring incorrectly.
const { data: { countryRegion } } = await axios.get(changeableUrl);
Do this in your api
const { data: countryRegion } = await axios.get(changeableUrl);
Update based on the comment:
If only country is needed in the array, just map thru it and extract countryRegion
export const fetchData = async country => {
try {
let { data: countryRegion } = await axios.get(url);
return { countryRegion: countryRegion.map(country => country.countryRegion) };
} catch (error) {
return error;
}
};

How to set route params for CRUD application using Redux and API server

I'm working on a React/Redux application that needs to make a simple GET request to an API server endpoint (/contents/{id}). Right now I have an action set up to fetch this data:
export const fetchInfoPage = id => {
return async dispatch => {
try {
const res = await fetch(`${server}/contents/${id}`)
if (res.ok) {
const json = await res.json()
await dispatch(fetchPageRequest())
await setTimeout(() => {
dispatch(fetchPageSuccess(json.data))
}, 1000)
} else {
const json = await res.json()
console.log(res, json)
}
} catch (error) {
dispatch(fetchPageFailure(error))
console.log(error)
}
}
}
And here's what fetchPageSuccess looks like:
const fetchPageSuccess = content => {
const { id, attributes } = content
return {
type: FETCH_PAGE_SUCCESS,
isFetching: false,
id: id,
name: attributes.name,
content: attributes.content,
created_by: attributes.created_by,
updated_by: attributes.updated_by,
created_at: attributes.created_at,
updated_at: attributes.updated_at
}
}
I am firing off this action inside of componentDidMount in my InfoPage component by using fetchInfoPage(match.params.name). The match.params.name is set up to match the parameters in the React Route (i.e. /:name/information). I want to instead change this to fetch the data by using an ID number from the JSON while still displaying :name as the route parameter.
I feel like I'm close in getting this wired up but there's a gap in my logic somewhere. Is it possible to do what I'm trying to do here? I also have access to a GET endpoint at /contents/slug/{slug}.
It's perfectly fine what you are trying to do.
Just map the id using your name in the fetchInfoPage from your json or you can actually send the id to your fetchInfoPage function from component. It has nothing to do with your route params. All you are doing is getting the name from your param and getting the corresponding id using your name. I assume you have a name: id map somewhere.
export const fetchInfoPage = name => {
return async dispatch => {
try {
const id = getIdFromName(name); // Write a helper function
const res = await fetch(`${server}/contents/${id}`)
if (res.ok) {
const json = await res.json()
await dispatch(fetchPageRequest())
await setTimeout(() => {
dispatch(fetchPageSuccess(json.data))
}, 1000)
} else {
const json = await res.json()
console.log(res, json)
}
} catch (error) {
dispatch(fetchPageFailure(error))
console.log(error)
}
}
}
Your route will still be /:name/information
What I ended up doing was fetching by slug instead. On the components where I fetched the data, I created the slug name in componentDidMount by using match.params.name from my route, then fired off fetchInfoPage(slugName) to get the data. I also cleaned up the code quite a bit so here's what fetchInfoPage looks like now:
export const fetchInfoPage = slug => {
return async dispatch => {
try {
dispatch(fetchPageRequest())
const res = await fetch(`${server}/contents/slug/${slug}`)
const contentType = res.headers.get('content-type')
if (contentType && contentType.includes('application/vnd.api+json')) {
const json = await res.json()
if (res.ok) {
dispatch(fetchPageSuccess(json))
} else {
printError(res, json)
dispatch(fetchPageFailure(res.body))
dispatch(push('/error'))
}
} else {
console.log('Not valid JSON')
dispatch(fetchPageFailure(res.body))
dispatch(push('/error'))
}
} catch (error) {
dispatch(fetchPageFailure(error))
dispatch(push('/error'))
console.log(`Network error: ${error.message}`)
}
}
}
And a componentDidMount example:
componentDidMount() {
const { match, fetchInfoPage } = this.props
const slugName = `${NAMESPACE}-${match.params.name}`
fetchInfoPage(slugName)
}

Resources