How to optimize APIs function with the same header - reactjs

I'm using fetch to deal with the API, I must add a header into every single API function like below. It's duplicate so much.
getSomething: async() => {
const user: IUserData = await JSON.parse(localStorage.getItem("token")!),
userCompanies: ICompanies = await JSON.parse(localStorage.getItem("companies")!),
companyId = Object.keys(userCompanies.Company)[0], meId = user.userInfo.data.id, myHeaders = new Headers();
myHeaders.append("authorization", user.accessToken.token);
myHeaders.append("company-id", companyId);
const requestOptions: RequestInit = {
headers: myHeaders,
method: "GET",
redirect: "follow"
};
return fetch(url + `/${meId}/calendars?type=tree`, requestOptions)
.then((response) => response.json())
.then((result) => result)
.catch((error) => error);
}
How can I reduce or optimize them?

Try abstracting fetch by making your own GET method, e.g.
async function get(url: string, options?: RequestInit) {
const user: IUserData = await JSON.parse(localStorage.getItem("token")!);
const userCompanies: ICompanies = await JSON.parse(localStorage.getItem("companies")!);
const companyId = Object.keys(userCompanies.Company)[0];
const meId = user.userInfo.data.id;
const myHeaders = new Headers();
myHeaders.append("authorization", user.accessToken.token);
myHeaders.append("company-id", companyId);
const baseOptions: RequestInit = {
headers: myHeaders,
method: "GET",
redirect: "follow"
};
// merge passed options with your baseOptions
let baseUrl = 'your-base-url';
return fetch(baseUrl + `/${meId}/${url}`, baseOptions)
.then((response) => response.json())
.then((result) => result)
.catch((error) => error);
}
getSomething: async() => {
return get('/calendars?type=tree')
}
This is just an idea, because I'm not sure which parameters are reusable over many different requests. You might even end up with baseGet which handles only core functionality shared for all requests, then have baseCompanyGet which is abstraction of baseGet and is used only by company requests, etc..
The idea is to identify code that is being repeated and put it in some kind of base method, while you pass only dynamic data via method parameters.

I guess the way you could do that is by grouping it in a storage class that you find helpful when accessing the data
//Setter
localStorage.setItem('myHeaders', data);
// getter
localStorage.getItem('myHeaders');
To work with local storage have a look at this article
https://medium.com/#siobhanpmahoney/local-storage-in-a-react-single-page-application-34ba30fc977d

Related

How can I implement the fetch implemented in the recoil selector to be executed when clicked?

I want to use a selector to execute the fetch implemented in the selector at a time I want. (click for example)
export const phoneRequestSelector = selector({
key : "request",
get : async ({get}) => {
const url = `${API_PATH}/goods`;
const phoneRequestInfo:PhoneRequestInfo = {
userName : get(nameState),
phoneNo : get(mobNoState),
}
const result = await fetch(url, {
method: 'POST',
body: JSON.stringify(phoneRequestInfo),
headers: {
'Content-Type': 'application/json',
},
}).then(response => {
return response;
})
return result;
}})
const requestData = useRecoilValueLoadable(phoneRequestSelector);
const onClickHandler = () => { //where I want it to run
I would be very grateful if you could tell me what changes I need to make in order to run the above code only when I want it.

Not receiving id from api call in a async loop

I'm trying to send an array of tweets and use the id returned from one tweet in the body of the next tweet in the array but it doesn't seem to be working.
The id is constantly 0 meaning the call doesn't update the id when each call is finished.
const postTweet = async () => {
let x = 0;
tweetData.forEach(async (data, i) => {
x = await fetch(`${process.env.REACT_APP_SERVER_URL}/new-tweet`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
cache: 'no-cache',
body: JSON.stringify({
status: data.tweetText,
in_reply_to_status_id: x
})
})
.then(res => res.ok ? res.json() : updateErrorStatus(true))
.then(({ replyToId }: { replyToId: number }) => replyToId)
})
}
Can anyone see where I'm going wrong?
The reason why x is not getting updated is that forEach does not wait for the promises to resolve (even if you use the await keyword), so all the requests are firing at the same time. Here's a little example to illustrate the point
const myAsyncFunc = ()=> new Promise((resolve) => {setTimeout(()=>{resolve('resolved')}, 1000)})
const arr=[1,2,3]
async function try1(){
arr.forEach(async i => {let r= await myAsyncFunc(); console.log("1 "+r);})
}
async function try2(){
for(const a of arr)
{
let r = await myAsyncFunc();
console.log("2 "+r)
}}
try1(); try2();

Call two fetch function with setState in between

I am trying to call two fetch APIs to store the information.
After first API call to cloudinary I get the URL back, which I want to store in state before hitting second API to store info in database. I am getting setAddOnData undefined error. While I try to make second API call , code functions properly for single API
const onSubmit = e => {
e.preventDefault();
console.log(fileInputRef.current.files[0]);
const myNewCroppedFile = fileInputRef.current.files[0];
console.log(myNewCroppedFile);
const formData = new FormData();
formData.append('file', myNewCroppedFile);
formData.append('upload_preset', 'xprl6rwq');
const options = {
method: 'POST',
body: formData
};
const addOnBody = JSON.stringify({ itemName, itemIconURL, itemPrice });
const config = {
headers: {
'Content-Type': 'application/JSON'
}
};
const option2 = {
method: 'POST',
body: addOnBody
};
return fetch(
'https://api.Cloudinary.com/v1_1/antilibrary/image/upload',
options
)
.then(res => res.json())
.then(res =>
setAddOnData({
...addOnData,
itemIconURL: res.secure_url
})
)
.then(fetch(`/api/v1/addOn`, option2, config))
.then(res => res.json());
};
const [addOnData, setAddOnData] = useState({
addOnCategory: '',
itemName: '',
itemPrice: '',
itemIconURL: '',
itemIconFileName: '',
step: 1
});
setState calls are asynchronous. When you run your second fetch call, the setAddOnData hasn't necessarily updated the addonData variable yet. You'd be best off moving the second fetch call to a useEffect which is dependent on the data returned from your first fetch call.
const addOnBody = JSON.stringify({ itemName, itemIconURL, itemPrice });
const config = {
headers: {
'Content-Type': 'application/JSON'
}
};
const option2 = {
method: 'POST',
body: addOnBody
};
const onSubmit = e => {
...
return fetch(
'https://api.Cloudinary.com/v1_1/antilibrary/image/upload',
options
)
.then(res => res.json())
.then(res =>
setAddOnData({
...addOnData,
itemIconURL: res.secure_url
})
)
};
useEffect( () => {
fetch(`/api/v1/addOn`, option2, config)
.then(res => res.json());
},[addOnData.itemIconURL])

Unable to get response using fetch in React

I am trying to call 3rd party API, to fetch some data. I am getting the response in Postman, but not getting expected response when I execute my code.
I tried in 2 ways. Both ways I am getting "Promise pending".What could be the reason??
//request.js
Method 1
export const callSearchGiftsAPI = inputs => dispatch => {
dispatch(searchGifts());
let url = new URL(GIFT_SEARCH_API_URL),
params = {
apiKey: GIFT_SEARCH_API_KEY,
query: inputs.item,
country: 'us',
itemsPerPage: 3
};
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));
return new Promise((resolve, reject) => {
setTimeout(() => resolve(
fetch(url, {
method: 'GET',
// mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
Authorization: `secret ${SECRET}`
}
})
.then(res => {
if (!res.ok) {
return Promise.reject(res.statusText);
}
console.log("hi", res.json());
return res.json();
})
.then(gifts => dispatch(searchGiftsSuccess(gifts)))
.catch(err => dispatch(searchGiftsError(err)))), 500)
});
}
Method 2:
export const callSearchGiftsAPI = inputs => dispatch => {
dispatch(searchGifts());
let url = new URL(GIFT_SEARCH_API_URL),
params = {
apiKey: GIFT_SEARCH_API_KEY,
query: inputs.item,
country: 'us',
itemsPerPage: 3
};
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));
fetch(url, {
method: 'GET',
// mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
Authorization: `secret ${SECRET}`
}
})
.then(res => {
if (!res.ok) {
return Promise.reject(res.statusText);
}
console.log('result', res.json());
return res.json();
})
.then(gifts => dispatch(searchGiftsSuccess(gifts)))
.catch(err => dispatch(searchGiftsError(err)));
};
//form.js
class Form extend React.Component{
onSubmit(values) {
const inputs = Object.assign({}, values);
return this.props.dispatch(callSearchGiftsAPI(inputs));
}
//Remaining code
}
Also please note that I have installed CORS plugin in Chrome, to allow the request.If I disable it and add mode:'no-cors' I am getting as 401 unauthorized.What else am I supposed to do?
What happens is that you are creating a new Promise and returning it, but you are not waiting for it to resolve. You can either use then of the new async/await syntax to get the correct result :
onSubmit = async values => {
const inputs = Object.assign({}, values);
return await this.props.dispatch(callSearchGiftsAPI(inputs));
}
The code above will work with your first method.
Since your second method does not return anything, you will never get your result, you need to return your fetch's result and apply the code I gave above :
return fetch(url, {
This worked.
I was trying to put console.log in the wrong place and hence was not able to see the response properly.
export const callSearchGiftsAPI = inputs => dispatch => {
dispatch(searchGifts());
let url = new URL(GIFT_SEARCH_API_URL),
params = {
apiKey: GIFT_SEARCH_API_KEY,
query: inputs.item,
country: 'us',
itemsPerPage: 3
};
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));
console.log(url);
return fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `secret ${SECRET}`
}
})
.then(res => {
console.log('result');
return res.json();
})
.then(response => {
console.log(response); // changed
dispatch(searchGiftsSuccess(response.items));
})
.catch(err => dispatch(searchGiftsError(err)));

React Native AsyncStorage: Get Token and Use it in a refactored API fetch

I have decided to put all my API calls in a seperate file and they are all stateless.
const get = endPoint => {
let token = "c8c17003468314909737ae7eccd83d4b6eecb792"; //I have put this token here manually
return fetch(endPoint, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Token " + token
}
}).then(response => response.json());
};
and in the same file i have the actual API calls. Example is as follows.
export const loadGroups = () => {
const endPoint = "https://xxxx.com/api/groups/";
return get(endPoint);
};
This works perfectly when i call the API from various components as follows.
import { loadGroups } from "../../api";
componentDidMount() {
loadGroups()
.then(responseJson => {
this.setState({
groups: responseJson
});
})
.catch(error => {
console.error(error);
});
}
However, I want to use AsyncStorage to retrieve a stored token and the due nature of it is to return a promise. This works well when i write the functions of getting the token and storing it in SetState in every component that I do the calls. I really want to refactor the code and using redux is a pain for me.
so far, i have written a file to get the token and it returns a promise.
import { AsyncStorage, Text } from "react-native";
const MyToken = async () => {
try {
const retrievedItem = await AsyncStorage.getItem("userToken");
const item = JSON.parse(retrievedItem);
return item;
} catch (error) {
return null;
}
};
export default MyToken;
And in the API file, I have re-written the code above to
const get = endPoint => {
MyToken().then(token => {
console.log(token, "try 1"); //this works
const lookupOptions = {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Token " + token
}
};
return fetch(endPoint, lookupOptions).then(response => {
console.log(response.json(), "promise response,json");
response.json();
});
});
};
However, whenever i call loadGroups().then() (like in the first example) function in any component, I get an errors that loadGroups.then() can't be resolved
Is there any way to resolve this without state, redux, mobx keeping in mind i want my API code in stateless functions in seperate modules.
In V2 of get function, you are not returning any Promise. Either put a return statement in get function like
const get = endPoint => {
return MyToken().then(
...
);
}
Or return Promise explicitly from that function, consider following code snippets
const get = endPoint => {
return new Promise((resolve, reject) => {
MyToken().then(token => {
...
fetch(endPoint, lookupOptions)
.then(response => response.json())
.then(resolvedResponse => {
resolve(resolvedResponse);
}).catch(error => {
reject(error);
});
});
});
};
Hope this will help!

Resources