How to optimally combine multiple axios responses - javascript

I am working with a React app. I have to create 2 objects using responses from 3 different APIs. For example:
DataObject1 will be created using API1 and API2
DataObject2 will be created using API1, API2, and API3
So, I am thinking about what would be the most optimal way of doing this by making sure 1 call each API only once.
I was thinking this:
const requestOne = axios.get(API1);
const requestTwo = axios.get(API2);
const requestThree = axios.get(API3);
axios.all([requestOne, requestTwo, requestThree]).then(axios.spread((...responses) => {
const dataObject1 = createDataObject1(responses[0], responses[1]);
const dataObject2 = createDataObject2(responses[0], responses[1], responses[2]);
// use/access the results
})).catch(errors => {
// react on errors.
})
const createDataObject1 = (response1, response2) => { //Combine required data and return dataObject1 }
const createDataObject2 = (response1, response2, response3) => { //Combine required data and return dataObject2 }
Is there a better way of doing this?

Looks fine.
You can change this
axios.all([requestOne, requestTwo, requestThree]).then(axios.spread((...responses) => {
const dataObject1 = createDataObject1(responses[0], responses[1]);
const dataObject2 = createDataObject2(responses[0], responses[1], responses[2]);
// use/access the results
})).catch(errors => {
// react on errors.
})
to
axios.all([requestOne, requestTwo, requestThree]).then((response) => {
const dataObject1 = createDataObject1(responses[0], responses[1]);
const dataObject2 = createDataObject2(responses[0], responses[1], responses[2]);
// use/access the results
}).catch(errors => {
// react on errors.
})
because it is unnecessary to spread and rest.
If you don't want to use them like responses[0], responses[1], etc then you can use:
axios.all([requestOne, requestTwo, requestThree]).then(axios.spread((response1, response2, response3) => {
const dataObject1 = createDataObject1(response1, response2);
const dataObject2 = createDataObject2(response1, response2,response3);
// use/access the results
})).catch(errors => {
// react on errors.
})

Are you using thunk middleware to make async calls in Redux? I don't want to assume that you are, but that seems like a good basic approach here.
const requestOne = axios.get(API1);
const requestTwo = axios.get(API2);
const requestThree = axios.get(API3);
Okay. So now requestOne.data has the result of making the axios get request. Or, would if the thunk creator was async and the code was const requestOne = await axios.get(API1);
Do you need to parse the data further from request___.data ?
If not you can just have
const dataObj1 = { response1: requestOne.data, response2: requestTwo.data }
const dataObj2 = { ... dataObject1, response3: requestThree.data };
Full answer:
// store/yourFile.js code
export const YourThunkCreator = () => async dispatch => {
try {
const const requestOne = await axios.get(API1);
// other axios requests
const dataObj1 = { response1: requestOne.data, response2: requestTwo.data }
const dataObj2 = { ... dataObject1, response3: requestThree.data };
// other code
dispatch(// to Redux Store);
} catch (error) {
console.error(error);
}

Related

Adding duplicates into array

I would like to be able to sort my array so I don't have duplicates into my "Recently-Viewed" section. The recently viewed section works fine except that it breaks when I add a duplicate. So I want to be able to sort my array so it doesn't break. I'm not really sure how to implement a sort function. Do I use filter or what do I do? I'm really confused.
My code:
const [tvShow, setTVShow] = useState([]);
const [recentlyViewed, setRecentlyViewed] = useState([]);
const getMovieRequest = async () => {
const url = `https://api.themoviedb.org/3/movie/top_rated?api_key=1e08baad3bc3eca3efdd54a0c80111b9&language=en-US&page=1`;
const response = await fetch(url);
const responseJson = await response.json();
setTVShow(responseJson.results)
};
useEffect(() => {
getMovieRequest();
},[]);
useEffect(() => {
const recentlyMovies = JSON.parse(localStorage.getItem('react-movie-app-favourites')
);
if (recentlyMovies) {
setRecentlyViewed(recentlyMovies.slice(0,5));
}
}, []);
const saveToLocalStorage = (items) => {
localStorage.setItem('react-movie-app-favourites', JSON.stringify(items))
};
const addRecentlyViewed = (movie) => {
const newRecentlyViewed = [movie, ...recentlyViewed]
setRecentlyViewed(newRecentlyViewed.slice(0,5));
saveToLocalStorage(newRecentlyViewed);
if (newRecentlyViewed > 5) {
newRecentlyViewed.pop();
}
};
Thank you guys in advance. I'm new to React and I find this very confusing.
Using the Set constructor and the spread syntax:
uniq = [...new Set(array)];
useEffect(() => {
const recentlyMovies = [...new Set(JSON.parse(localStorage.getItem('react-movie-app-favourites')))];
if (recentlyMovies) {
setRecentlyViewed(recentlyMovies.slice(0,5));
}
}, []);

How to show loader during multiple api calls in a loop in React

I have two API's
First API returns list of items which I am iterating to get each item's detailed data.
Here's the code
const [loader, setLoader] = useState(false);
React.useEffect(() => {
const fetchUsers = async() => {
setLoader(true);
const users = await getUsers();
const promises = users.map(async (user) => {
let userData = await getUsersDetailedData(user.userId);
return userData
});
let finalUsers = await Promise.all(promises);
setLoader(false);
}
fetchUsers();
}, [])
I am updating loader state before the api call and after call but it is not working.
Loader state is updating these many times and loader is not displaying
logs
Try it in this way,
React.useEffect(() => {
const fetchUsers = async() => {
const users = await getUsers();
const promises = users.map(async (user) => {
let userData = await getUsersDetailedData(user.userId);
return userData
});
let finalUsers = Promise.all(promises);
return finalUsers;
}
setLoader(true);
fetchUsers().then(res=>{
setLoader(false);
});
}, [])

React jsonserver promise result issue

I am creating a react/ redux app with json fake api server I am trying to add a login and trying to get data from json fake api server, data is showing and all ok , but data is always resulting as a promise and the required data is inside the promise. i tried many ways to distructure but throwing errors , could anyone help me on this,
my axios request
const urlss = "http://localhost:5000/users";
export const userslist = async () => {
const r = await axios.get(urlss);
const data = r.data;
return data;
};
const newout2 = userslist();
const newout = newout2;
console.log(newout);
the place where I am using it
export const login = (credentials) => (dispatch) => {
return new Promise((resolve, reject) => {
const matchingUser =
newout2 &&
newout2.find(({ username }) => username === credentials.username);
if (matchingUser) {
if (matchingUser.password === credentials.password) {
dispatch(setUser(matchingUser));
resolve(matchingUser);
} else {
dispatch(setUser(null));
reject("Password wrong");
}
} else {
dispatch(setUser(null));
reject("No user matching");
}
});
};
i am getting this error
You are using then in your userslist method while awaiting in an async method. drop the then and just use proper await inside an async method.
const urlss = "http://localhost:5000/users";
export const userslist = async () => {
const r = await axios.get(urlss);
const data = r.data;
return data;
};

Jest, How to mock an imported callback in Object Destructuring?

I did the tests in the following way, but it bothers me how I need to import an object with the functions, instead of just importing the functions.
this works
//service.test.js
const get = require('../../modules/bankUser/model/getRegisteredUser');
get.getRegisteredUser = jest.fn()
.mockImplementationOnce(async () => mock)
.mockImplementationOnce(async () => mockUpdated);
//service.js
const get = require('../model/getRegisteredUser');
const testedFunction () => {
const depositReciver = await get.getRegisteredUser(depositName, depositCpf);
}
this dosent
//service.test.js
const get = require('../../modules/bankUser/model/getRegisteredUser');
get.getRegisteredUser = jest.fn()
.mockImplementationOnce(async () => mock)
.mockImplementationOnce(async () => mockUpdated);
//service.js
const { getRegisteredUser } = require('../model/getRegisteredUser');
const testedFunction () => {
const depositReciver = await getRegisteredUser(depositName, depositCpf);
}
I'm triyng this (way):
//service.test.js
const get = require('../../modules/bankUser/model/getRegisteredUser');
jest.mock('../../modules/bankUser/model/getRegisteredUser');
get.mockImplementationOnce(() => ({ getRegisteredUser: () => mockObject }));
jest returns this:
TypeError: get.mockImplementationOnce is not a function
also I've tried to import like this
//service.test.js
const { getRegisteredUser } = require('../../modules/bankUser/model/getRegisteredUser');
jest.mock('../../modules/bankUser/model/getRegisteredUser');
getRegisteredUser.mockImplementationOnce(() => ({ getRegisteredUser: () => mockObject }));
EDIT
//getRegisteredUser.js
const { getConnection } = require('../../../global/connection');
const getRegisteredUser = async (userName, cpf) => {
const db = await getConnection('Data-Base');
const res = await db.collection('Collection')
.findOne({ userName, cpf });
return res;
};
module.exports = { getRegisteredUser };
The example you are pointing [https://jestjs.io/docs/es6-class-mocks#replacing-the-mock-using-mockimplementation-or-mockimplementationonce][1]
is related to the default export but I think you are using named export in your code.
There are multiple ways to do so
One Way
const get = require('../../modules/bankUser/model/getRegisteredUser');
jest.mock('../../modules/bankUser/model/getRegisteredUser');
get.getRegisteredUser.mockImplementationOnce(() => mockObject);
Second Way
jest.mock('../../modules/bankUser/model/getRegisteredUser', () => ({
...jest.requireActual('../../modules/bankUser/model/getRegisteredUser'),
getRegisteredUser: jest.fn().mockImplementation(() => mockObject),
}))
An Another Way
We can also mock a module using the __mocks__ directory which is inbuilt feature of jest
And at the end never forgot to call clearAllMocks() after each test else it may impact the output of other tests.
afterEach(() => {
jest.clearAllMocks();
});

How to fetch data from multiple urls at once?

I have a function that fetches from a url in React
const DataContextProvider = (props) => {
const [isLoading, setLoading] = useState(false);
const [cocktails, setCocktails] = useState([]);
useEffect(() => {
const fetchCocktailList = async () => {
const baseUrl = 'https://www.thecocktaildb.com/api/json/v1/1/';
setLoading(true);
try {
const res = await fetch(`${baseUrl}search.php?s=margarita`);
const data = await res.json();
console.log(data);
setCocktails(data.drinks);
setLoading(false);
} catch (err) {
console.log('Error fetching data');
setLoading(false);
}
};
fetchCocktailList();
}, []);
How I'm mapping data so far.
const DrinkList = () => {
const { cocktails } = useContext(DataContext);
return (
<div className='drink-list-wrapper'>
{cocktails.length > 0 &&
cocktails.map((drink) => {
return <DrinkItem drink={drink} key={drink.idDrink} />;
})}
</div>
);
};
However I want to fetch from this url also ${baseUrl}search.php?s=martini
I would like a good clean way to do this and set my state to both of the returned data.
First base the data fetch function on a parameter:
const fetchCocktail = async (name) => {
const baseUrl = 'https://www.thecocktaildb.com/api/json/v1/1/';
try {
const res = await fetch(`${baseUrl}search.php?s=` + name);
const data = await res.json();
return data.drinks;
} catch (err) {
console.log('Error fetching data');
}
}
Then use Promise.all to await all results:
setLoading(true);
var promises = [
fetchCocktail(`margarita`),
fetchCocktail(`martini`)
];
var results = await Promise.all(promises);
setLoading(false);
DrinkList(results);
Where results will be an array with the responses that you can use on the DrinkList function.
Here's a method which will let you specify the cocktail names as dependencies to the useEffect so you can store them in your state and fetch new drink lists if you want new recipes. If not, it'll just be a static state variable.
I've also added another state variable errorMessage which you use to pass an error message in the case of failure.
Also, you should include the appropriate dependencies in your useEffect hook. The setState functions returned by calls to useState are stable and won't trigger a re-run of the effect, and the cocktailNames variable won't trigger a re-run unless you update it with new things to fetch.
const DataContextProvider = (props) => {
const [isLoading, setLoading] = useState(false);
const [cocktails, setCocktails] = useState([]);
const [errorMessage, setErrorMessage] = useState(''); // holds an error message in case the network request dosn't succeed
const [cocktailNames, setCocktailNames] = useState(['margarita', 'martini']); // the search queries for the `s` parameter at your API endpoint
useEffect(() => {
const fetchCocktailLists = async (...cocktailNames) => {
const fetchCocktailList = async (cocktailName) => {
const baseUrl = 'https://www.thecocktaildb.com/api/json/v1/1/search.php';
const url = new URL(baseUrl);
const params = new URLSearchParams({s: cocktailName});
url.search = params.toString(); // -> '?s=cocktailName'
const res = await fetch(url.href); // -> 'https://www.thecocktaildb.com/api/json/v1/1/search.php?s=cocktailName'
const data = await res.json();
const {drinks: drinkList} = data; // destructured form of: const drinkList = data.drinks;
return drinkList;
};
setLoading(true);
try {
const promises = [];
for (const cocktailName of cocktailNames) {
promises.push(fetchCocktailList(cocktailName));
}
const drinkLists = await Promise.all(promises); // -> [[drink1, drink2], [drink3, drink4]]
const allDrinks = drinkLists.flat(1); // -> [drink1, drink2, drink3, drink4]
setCocktails(allDrinks);
}
catch (err) {
setErrorMessage(err.message /* or whatever custom message you want */);
}
setLoading(false);
};
fetchCocktailList(...cocktailNames);
}, [cocktailNames, setCocktails, setErrorMessage, setLoading]);
};
var promises = [
fetchCocktail(api1),
fetchCocktail(api2)
];
var results = await Promise.allSettled(promises);

Categories