How to send request using customized Hooks in React Hooks - reactjs

I was trying to create a custom Hooks for handling input HTTP request from any component by simply calling the useHttpPOSTHandler and want to use .then with Axios but its getting failed and error is
as i am new in react not able to debug this
what i have tried
import { useEffect, useState } from "react";
import axios from "axios";
const useHttpPOSTHandler = ({url , data}) => {
const [httpData, setHttpData] = useState();
const apiMethod = useCallback( ({url , data}) => {
axios
.post(url , data)
.then((response) => {
console.log(response)
console.log(response.data)
setHttpData(response.data);
})
.catch((error) => {
console.log(error);
});
}, [setHttpData])
return [ httpData , apiMethod];
};
export default useHttpPOSTHandler;

The arguments to useHTTPPostHandler are expected to be an object with keys url and data instead you are passing them individually causing a syntax error, wrap them within {}
const getData = useHttpPOSTHandler(
{ url: 'url', data: { "password": userPassword, "username": userName }
});
EDIT: As per your update, you won't see the updated data as soon as you make an API call. It will reflect in the next render
import useHttpPOSTHandler from "...."
const MyFunc = () => {
const [httpData, apiMethod] = useHttpPOSTHandlerdotthen()
const handleSubmit = () => {
apiMethod({url: 'url' , data: { "password": userPassword, "username": userName }})
}
if(httpData){
console.log("entered in api method")
console.log(httpData)
}
return (
<div>
...
</div>
)
}

Related

json response from mock server not printing but is in the console

I am trying to learn react, and I am making a successful API call, but it only prints in the console. I found examples but many of them recommended to use setData(json) but I am not able to use it because the file is a list of export async function which was also recommended.
export async function GetHellWorld() {
return fetch(`http://localhost:8080/api`, {
method: "Get",
headers: {
"Content-type": "application/json; charset=UTF-8"
}
}).then(response => response.json())
.then(json => {
console.log(json)
})
.catch(error => (console.log(error)))
}
and the component
function Test(thisArg, argArray) {
const result = GetHellWorld.apply()
return (
<div className="App">
{JSON.stringify(result)}
</div>
);
}
export default Test;
In the console I see "Hello World" but in the browser is get just {}.
Two questions:
How can I bind the JSON response to an object so I can do something like result.name.
Is this the correct was to call the await function? const result = GetHellWorld.apply()
---- update ----
I decided to try axios because I want to make multiple calls in one file.
const axios = require('axios');
export class AppService {
public async GetHelloWorld(): Promise<any> {
const response = await axios.get(`http://localhost:8080/api`, {
method: "Get",
headers: {
"Content-type": "application/json; charset=UTF-8"
}
}).catch(() => console.log("Issue in GetHelloWorld"))
return response.data
}
}
component
import React from 'react';
import {AppService} from "../services/app.service";
function Movies() {
const api = new AppService()
const hello = async () => {
const response = await api.GetHelloWorld();
console.log("The response: " + response)
}
return (
<div className="App">
{JSON.stringify(hello)}
</div>
);
}
note I had to add typescript support.
For whatever reason I get
Module not found: Error: Can't resolve '../services/app.service' in '/Users/miketye/programming/test-react/src/components'
While the other answer about using a custom hook can work, I would not recommend it while you're still leaning React.
Look up how to use the "useEffect" hook, that's generally how you want to do any sort of loading logic in React.
First off, you need to fix your async function so it actually returns a value:
// style/convention note, but non-component functions should not start with a capital letter
export async function getHelloWorld() {
return fetch(`http://localhost:8080/api`, {
method: "Get",
headers: {
"Content-type": "application/json; charset=UTF-8"
}
}).then(response => response.json())
.then(json => {
return json // will cause this function to return a Promise of type "string", since we're in an async function
})
// better to just let the error get thrown here, for testing
}
Then use it like this:
function Test(thisArg, argArray) {
[fetchResult, setFetchResult] = useState(undefined) // look up useState. State is how you have values that change over time in a resct component
useEffect(() => {
async function fetchData() {
const data = await getHelloWorld()
setFetchResult(data)
}
fetchData()
}, [])
// look up useEffect. Since the second argument (the "dependency array") is empty, useEffect will fire only once, after the component loads
return (
<div className="App">
{result ? JSON.stringify(result) : "no result yet"}
</div>
);
}
export default Test;
You can use a custom hook for this purpose:
import { useState } from "react";
const useFetchData = () => {
const [data, setData] = useState(null);
const fetchData = () => {
fetch("http://localhost:8080/api", {
method: "Get",
headers: {
"Content-type": "application/json; charset=UTF-8"
}
}).then(response => response.json())
.then(json => { setData(json); })
.catch(error => { console.log(error); })
}
useEffect(() => {
fetchData();
}, []);
return { data, fetchData };
}
export default useFetchData;
And then call it in your component:
import useFetchData from "#/hooks/useFetchData";
const Test = () => {
const { data, fetchData } = useFetchData();
// CALL fetchData IF YOU WANT TO UPDATE THE CURRENT STATE
return (
<div className="App">
{data && JSON.stringify(data)}
</div>
);
}
export default Test;

Access React context in an API service

In my React application I use the context API to store the user information through the useContext hook:
const AuthContext = createContext<AuthContextType>(null!);
const useAuth = () => useContext(AuthContext);
function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User>();
// Implementations of values
const value = useMemo(() => ({ user, login, logout }), [user]);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export { AuthProvider, useAuth };
Accessing the auth information works all fine and dandy in the components:
export default function CoolComponent() {
const auth = useAuth();
if (auth.user) {
// Do something
}
return <div>Hello {auth.user}</div>;
}
The thing is that my jwt-token is stored in the user object and I need it for my API calls in my service, but hooks are not allowed outside functional components. Can I circumvent this in a clever way? Some things that I can think of is to pass the token on every call to the service (not very DRY) or save the token in localStorage and then retrieve it from there in the service, but it seems unnecessary to store the same information in two different places?
Update:
Now with the service code:
const baseUrl = environment.apiUrl;
function getToken() {
// This is what I would like to get some help with
}
const headers = {
...(getToken() && { Authorization: `Bearer ${getToken()}` }),
"Content-Type": "application/json",
};
function getAllProjects(): Promise<IProject[]> {
return fetch(`${baseUrl}projects`, {
headers,
}).then((response) => response.json());
}
function createProject(project: CreateProjectDTO): Promise<IProject> {
return fetch(`${baseUrl}projects`, {
method: "POST",
headers,
body: JSON.stringify(project),
}).then((response) => response.json());
}
// + many more
export { getAllProjects, createProject };
Calling the service in a component:
useEffect(() => {
const fetchProjects = async () => {
setIsLoading(true);
try {
const allProjects = await getAllProjects();
setProjects(allProjects);
} catch (error) {
// Handle error
} finally {
setIsLoading(false);
}
};
fetchProjects();
}, []);
The React documentation says that you cannot call hooks inside JavaScript functions.
What can you do?
Use custom hooks. rename functions as useCreateProject and return your function. Then you will be able to call useAuth inside your custom hook:
const useCreateProject =() =>{
const {user} = useAuth();
function createProject(project: CreateProjectDTO): Promise<IProject> {
return fetch(`${baseUrl}projects`, {
method: "POST",
headers,
body: JSON.stringify(project),
}).then((response) => response.json());
}
return createProject
}
Then call it like this:
const createProject = useCreateProject()
useEffect(() => {
const create = async () => {
setIsLoading(true);
try {
await createProject()
} catch (error) {
// Handle error
} finally {
setIsLoading(false);
}
};
create();
}, []);
But my advice is to store the token on localStorage or in cookies. Context data will be lost when user refreshes page. However, if that is not case for you, you can continue using context.

Writing integration test for asynchronous code in React application with Context and useEffect

So, in useEffect I am fetching an object from the API then I am dispatching response data to the Context reducer and then updating the state. It looks something like this:
export const fetchItem = (id) => request({url: `/items/${id}`, method: 'get'});
...
const {dispatch, singleItem} = useProvider();
useEffect(() => {
const id = getItemIdFromUrl(props);
fetchItem(id).then((response) => {
dispatch(action(response.data.data));
});
}, [props, dispatch]);
I would like to write a good integration test for this. I am using react-testing-library with Jest. I am trying to mock the return value of the fetchItem function and then to check if everything is rendered correctly but constantly getting this warning:
act(() => {
/* fire events that update state */
});
/* assert on the output */
Any chance to do this correctly?
This is how the request method looks like:
import axios from 'axios';
import humps from 'humps';
import {getItem} from './localStorage';
const api = axios.create({
baseURL: process.env.REACT_APP_API_URL,
});
api.interceptors.response.use(
(response) => humps.camelizeKeys(response),
(error) => Promise.reject(error.response),
);
api.interceptors.request.use(
(request) => {
request.data = humps.decamelizeKeys(request.data);
return request;
},
(error) => Promise.reject(error.request),
);
export default function request({url, method, headers = {}, data}) {
try {
const token = getItem('token');
headers.Authorization = token;
return api({method, url, headers, data});
} catch (error) {
if (error.status === 500) {
console.log('HANDLE ERROR: ', error);
}
throw error;
}
}

How to use fetch function and show api response on component ? Custom fetch response is not passing to component

After Fetch /Vehicles from api I wanted to show them in component but inside useFetch function I can console.log res.data but inside the Trucks component I could not map trucks.
My Custom useFetch function:
import { useState, useEffect } from 'react';
import axios from 'axios';
const useFetch = (url) => {
const api = axios.create({
baseURL: 'api url',
headers: { Authorization: `Bearer ${localStorage.token}` },
});
const [data, setData] = useState(null);
const [isPending, setIsPending] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortCont = new AbortController();
setTimeout(() => {
api
.get(url)
.then((res) => {
console.log(res.data);
if (!res.ok) {
throw Error('Could not with fetch data');
}
})
.then((data) => {
data.json();
console.log(data);
setIsPending(false);
setError(null);
})
.catch((err) => {
if (err.name === 'AbortError') {
console.log('fetch aborted');
} else {
setIsPending(false);
setError(err.message);
}
});
}, 1000);
return () => abortCont.abort();
}, [url]);
return { data, isPending, error };
};
export default useFetch;
I used this custom useFetch function and could get data (inside the function) its not usable to render
Some possible reasons I thought ;
useFetch function may not return data properly
I may need to use stringify or json function for API respond
Trucks Component:
const Trucks = () => {
const { data: trucks, error, isPending } = useFetch('/vehicles');//
console.log('they are ' + { trucks }); //console output ->[object,Object]
return (Some Jsx)};
export default Trucks;
Thanks
There are two issues with your Hook itself that I can see.
The first thing is that you never call setData. This means that although you are fetching the data inside your hook and consoling it out, you never set the data object to hold the retrieved data.
The second thing is that the second then isn't actually doing anything with the data you are retrieving. You can handle this all inside the first statement as such
.then((res) => {
setData(res.data);
setIsPending(false);
if (!res.ok) {
setIsPending(false);
throw Error("Could not with fetch data");
}
})
Once you do this you should be able to call your hook inside the related component, and render it using some JSX. Just make sure to add a check for isPending and display some sort of loading screen before actually rendering your data.
Another note, when you use axios, you do not need to call data.json(). That is something that is required for the fetch API but axios just requires you to call res.data when resolving the promise

How to create a custom Hooks in reactjs hooks?

I was trying to create a custom Hooks for handling input HTTP request from any component by simply calling the useCustomHooks but its getting failed and error is
Can not use keyword 'await' outside an async function
All i made is a handler that triggers http request custom component method
import { useState } from 'react';
import axios from "axios";
const useHttpReqHandler = () => {
const [result, setResult] = useState();
const apiMethod = async ({url , data , method}) => {
let options = {
method,
url,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=UTF-8'
},
data
};
let response = await axios(options);
const UpdatedData = await response.data;
console.log(UpdatedData)
setResult(UpdatedData);
}
return [result, apiMethod];
};
export default useHttpReqHandler;
Now i can use this hook in my code and on any event handler just call callAPI returned from the hook like this
const MyFunc = () => {
const [apiResult, apiMethod] = useHttpReqHandler();
const captchValidation = () => {
const x = result.toString();;
const y = inputValue.toString();;
if ( x === y) {
apiMethod({url: 'some url here', data: {"email": email}, method: 'post'});
alert("success")
}
else {
alert("fail")
}
}
Is is a correct approch ? as i am beginner in Reactjs
Here is a working version:
useHttpReqHandler.jsx
import { useState } from 'react';
import axios from "axios";
const useHttpReqHandler = () => {
const [apiResult, setApiResult] = useState();
const apiMethod = async ({url , data , method}) => {
let options = {
method,
url,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=UTF-8'
},
data
};
let response = await axios(options);
let responseOK = response && response.status === 200 && response.statusText === 'OK';
if (responseOK) {
const data = await response.data;
console.log(data)
setApiResult(data);
}
}
return [apiResult, apiMethod];
};
export default useHttpReqHandler;
What's important here:
await is called inside an async function (apiMethod)
The result is stored in a local state (apiResult)
The function returns an array [apiResult, apiMethod]
How to use it:
const [apiResult, apiMethod] = useHttpReqHandler();
apiMethod({url: 'some url here', data: {"email": email}, method: 'post'});
Render the result:
return {apiResult};
In my opinion, it is better to use .then with Axios. and try to create for each method different functions "Get/Post...", why because in the GET method you need to useEffect, but it can not be the same case in POST method. in GET method useHttpReqHandler.js
import { useEffect, useState } from "react";
import axios from "axios";
// GET DATA
const useHttpReqHandler = (url) => {
const [httpData, setHttpData] = useState();
useEffect(() => {
axios
.get(url)
.then((axiosData) => {
// Axios DATA object
setHttpData(axiosData.data);
// you can check what is in the object by console.log(axiosData);
// also you can change the state, call functions...
})
.catch((error) => {
console.log(error);
});
}, []);
return httpData;
};
export default useHttpReqHandler;
in your main file
import useHttpReqHandler from "...."
const MyFunc = () => {
const getData = useHttpReqHandler("URL");
return (
<div>
...
</div>
)
}
I hope it helps
the same thing will be with POSt, PUT, DELETE ... you will create functions for each method that will handle the Http req

Resources