i need to call my custom hook called useAxios() inside function. As i know its not available in react, but i hope there's some method that do the trick
useAxios hook:
import { useState, useEffect } from "react";
import Axios from "axios";
const useAxios = (method: string, param: string, data: any) => {
const [response, setResponse] = useState([]);
const [error, setError] = useState([]);
const fetchData = () => {
Axios({
method: method,
url: `https://gernagroup-server.herokuapp.com/${param}`,
data: data,
})
.then((response) => {
setResponse(response.data);
})
.catch((err) => {
setError(err);
});
};
useEffect(() => {
fetchData();
}, []);
return { response, error };
};
export default useAxios;
I have to call this function, but using my hook
const handleNewSale = () => {
Axios.post("https://gernagroup-server.herokuapp.com/new-sale", {
data: selectedValues,
})
.then((response) => {
console.log(response);
showModal(ResultType.success, "New sale added successfully!");
})
.catch((err) => {
console.log(err);
showModal(ResultType.error, "Something went wrong");
});
};
I have to call this function on click
<Button text="New sale" onClick={handleNewSale} />
fetchData is async, you’re missing async and await keywords in code. Same for handleNewSale.
Avoid any types.
Give your custom hook a strict return type. Also you may need an export keyword in front of the ‘const’
Related
I created a file Category.js
import React, { useState } from 'react'
export const CategoryData = (props) => {
const [Category, setCategory] = useState('')
fetch('https://www.amrutras.com/Category.php')
.then((response) => response.json())
.then((responseJson) => {
{
setCategory(responseJson)
// responseJson.map((item) => Alert.alert(item.Name))
}
// Showing response message coming from server after inserting records.
})
.catch((error) => {
console.error(error)
})
return Category
}
export default CategoryData
I want to use this Category const in my other component.
I tried to do that with
import CategoryData from '../consts/CategoryData'
and using this function in useEffect of another component. like this.
useEffect(() => {
console.log(CategoryData)
})
But it's not working.
You cannot use hooks directly within functions, unless they are custom hooks which you then cannot invoke inside other hooks but have to follow the rules of hooks to use them
You can restructure your code to implement CategoryData like a custom hook
export const useCategoryData = (props) => {
const [Category, setCategory] = useState('')
useEffect(() => {
fetch('https://www.amrutras.com/Category.php')
.then((response) => response.json())
.then((responseJson) => {
{
setCategory(responseJson)
// responseJson.map((item) => Alert.alert(item.Name))
}
// Showing response message coming from server after inserting records.
})
.catch((error) => {
console.error(error)
})
}, [])
return Category
}
export default useCategoryData;
Now you can use it in your component like
function App () {
const categoryData = useCategoryData();
...
}
SOLUTION2:
Another way to implement this is to not use custom hook but implement a normal function like
export const CategoryData = (props) => {
return fetch('https://www.amrutras.com/Category.php')
.then((response) => response.json())
})
.catch((error) => {
console.error(error)
})
}
export default CategoryData
and use it like
function App () {
const [categoryData, setCategoryData] = useState(null);
useEffect(() => {
CategoryData.then(res => setCategoryData(res));
}, []); Make sure to provide a dependency list to your useEffect otherwise you will end up in a infinite loop
}
Make it as a custom hook
import React, { useState } from 'react'
export const useCategoryData = (props) => {
const [Category, setCategory] = useState('')
useEffect(()=>{
fetch('https://www.amrutras.com/Category.php')
.then((response) => response.json())
.then((responseJson) => {
{
setCategory(responseJson)
// responseJson.map((item) => Alert.alert(item.Name))
}
// Showing response message coming from server after inserting records.
})
.catch((error) => {
console.error(error)
})
},[])
return {Category}
}
import it like
import {useCategoryData} from '.......'
const {Category} = useCategoryData()
I have a lot of react experience but I'm new to hooks.
I have the following useFetch hook that I modified after this useAsync hook:
import { useState, useEffect, useCallback } from 'react'
export default function useFetch(url, options, { immediate }) {
const [data, setData] = useState(null)
const [error, setError] = useState(null)
const [isPending, setIsPending] = useState(false)
const executeFetch = useCallback(async () => {
setIsPending(true)
setData(null)
setError(null)
await fetch(url, options)
.then((response) => response.json())
.then((response) => setData(response))
.catch((err) => setError(err))
.finally(() => setIsPending(false))
return { data, error, isPending }
}, [url, options, data, error, isPending])
useEffect(() => {
if (immediate) {
executeFetch()
}
}, [executeFetch, immediate])
return { data, error, isPending, executeFetch }
}
My problem is I want to use it inside a submit function, and hooks don't work inside other functions, like so (reduced version of the code for brevity):
export default function SignupModal({ closeModal }) {
const { executeFetch } = useFetch(url, {options},
{ immediate: false }
)
async function handleSubmit(evt) {
evt.preventDefault()
const { data, error, isPending } = await executeFetch()
}
...
}
currently I'm intentionaly throwing an error in the call, but the error variable remains null.
What am I missing here?
Is this even possible with hooks?
Thanks in advance!
React hook can only be used in the body of your component not inside another function. executeFetch itself is returning { data, error, isPending } and this makes it a nested hook so you can't use it inside your handleSubmit.
useFetch is already returning { data, error, isPending, executeFetch } so executeFetch doesn't need to return again. You can access all these data from the useFetch hook. When you call executeFetch data in your component, data, error and isPending will be updated by setState which will cause your hook to return a new set of values for any of these values that get updated.
export default function useFetch(url, options, { immediate }) {
const [data, setData] = useState(null)
const [error, setError] = useState(null)
const [isPending, setIsPending] = useState(false)
const executeFetch = useCallback(async () => {
setIsPending(true)
setData(null)
setError(null)
await fetch(url, options)
.then((response) => response.json())
.then((response) => setData(response))
.catch((err) => setError(err))
.finally(() => setIsPending(false))
}, [url, options, data, error, isPending])
useEffect(() => {
if (immediate) {
executeFetch()
}
}, [executeFetch, immediate])
return { data, error, isPending, executeFetch }
}
export default function SignupModal({ closeModal }) {
const { executeFetch, data, error, isPending } = useFetch(url, {options},
{ immediate: false }
)
async function handleSubmit(evt) {
evt.preventDefault()
await executeFetch()
}
...
// Example in your return function
{error != null && <Error />}
<Button state={isPending ? 'processing' : 'normal'}
}
Updated based on the comment
If you need to have an access to data or error inside your handleSubmit function, you will need to return the promise's response/error in your hook so then you should be able to access data/error inside your handleSubmit as well.
Also I recommend to pass options or any other variable data that are subject to change before user triggers handleSubmit to the executeFetch as an argument so executeFetch can always get the latest data.
CodeSandBox Example 1
CodeSandBox Example 2
const useFetch = url => {
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const [data, setData] = useState(null);
const executeFetch = useCallback(
// Here you will access to the latest updated options.
async ({ options }) => {
setIsPending(true);
setError(null);
return await fetch(url, options)
.then(response => response.json())
.then(response => {
setData(response);
return response;
})
.catch(err => {
setError(err.message)
return err;
})
.finally(() => setIsPending(false));
},
[url, setIsPending, setError]
);
return { data, error, isPending, executeFetch }
};
const { data, executeFetch, error, isPending } = useFetch("URL");
const handleSubmit = useCallback(async (event) => {
event.preventDefault();
// I am passing hardcoded { id: 1 } as an argument. This can
// be a value from the state ~ user's input depending on your
// application's logic.
await executeFetch({ id: 1 }).then(response => {
// Here you will access to
// data or error from promise.
console.log('RESPONSE: ', response);
})
}, [executeFetch]);
Another recommendations is to not pass a boolean to trigger executeFetch immediately inside your hook, it's up to the caller to decide whether to run the executeFetch immediately or not.
const { executeFetch, ... } = useFetch(....);
// you can call it immediately after setting the hook if you ever needed
await executeFetch()
I am writing a custom react hook for fetching data from an endpoint. This is what the function looks like
import { useState } from "react";
const useHttp = async (endpoint, method, data) => {
const [loading, setLoading] = useState(false)
const [fetchedData, setfetchedData] = useState(null)
setfetchedData(await fetch.method(endpoint));
return [isLoading, fetchedData]
}
export default useHttp;
As you can see, I want to do a fetch request to whatever method is passed on to the useHttp hook. Please someone point me how to do it?
You cannot pass async functions to React Hooks. You have to useEffect
import { useState, useEffect } from "react";
const useHttp = (endpoint, method, options) => {
const [isLoading, setLoading] = useState(false);
const [fetchedData, setFetchedData] = useState(null);
useEffect(() => {
setLoading(true);
fetch(endpoint, { method, ...options })
.then(data => data.json())
.then((json) => {
// do something with JSON data
setFetchedData(json);
})
.catch((err) => {
// do something with err
})
.finally(() => {
setLoading(false);
});
}, []);
return [isLoading, fetchedData];
};
export default useHttp;
Use useEffect hook to make the HTTP request.
fetch function takes an optional second argument which is an object specifying various options for the HTTP request and one of the options is a method option. Use this method option to specify the request method.
import { useState, useEffect } from "react";
const useHttp = async (endpoint, method, data) => {
const [loading, setLoading] = useState(false);
const [fetchedData, setfetchedData] = useState(null);
useEffect(() => {
setLoading(true);
fetch(endpoint, { method })
.then(res => res.json())
.then(data => {
setLoading(false);
setfetchedData(data);
})
.catch(err => {
setLoading(false);
console.log(err.message);
});
}, []);
return [isLoading, fetchedData];
}
For details on how to specify options for fetch function and different options that can be specified, see using fetch
If you want to use async-await syntax, you can write useEffect hook as:
useEffect(() => {
async function makeRequest() {
setLoading(true);
try {
const response = await fetch(endpoint, { method });
const data = await res.json();
setLoading(false);
setfetchedData(data);
} catch (error) {
setLoading(false);
console.log(err.message);
}
}
makeRequest();
}, []);
hi maybe this help you:
1- call function:
const useHttp = async (url,method,data)=>{
var options = {
method:method,
headers: {
'Content-Type': 'application/json; charset=utf-8;'
}
};
if(method==='POST' && data)
options.body = JSON.stringify(data);
const response = await fetch(url, options);
const rep = await response.json();
console.log(rep);
return rep;
};
in above code first create your request options and then send it by fetch to end point.
2- use it in compoent like below:
setLoading(true);
var rep = await useHttp(...)
setLoading(false);
I'm quite new to react and trying to ease some development.
I'm having this custom hook useApi.
import {useState} from "react";
import {PetsApiFactory} from "petstore-axios-api"
import {useKeycloak} from "#react-keycloak/web";
const petsApi = new PetsApiFactory({}, `${process.env.REACT_APP_BASE_URL}`);
export const useApi = () => {
const [isLoading, setIsLoading] = useState(false);
const {keycloak} = useKeycloak();
const createPets = (requestData) => {
setIsLoading(true);
return petsApi.createPets(requestData, {
headers: {
Authorization: `Bearer ${keycloak.token}`
}
}).finally(() => setIsLoading(false));
};
const listPets = (limit = undefined) => {
setIsLoading(false);
return petsApi.listPets(limit, {
headers: {
Authorization: `Bearer ${keycloak.token}`
}
}).finally(() => setIsLoading(false));
};
const showPetById = (petId) => {
setIsLoading(true);
return petsApi.showPetById(petId, {
headers: {
Authorization: `Bearer ${keycloak.token}`
}
}).finally(() => setIsLoading(false));
};
return {
createPets,
listPets,
showPetById,
isLoading
}
};
I'd like to call it from within another component like in this snippet.
useEffect(() => {
listPets()
.then(result => setPetsData(result.data))
.catch(console.log)
}, []);
However react is telling me that I'm missing the dependency on listPets
React Hook useEffect has a missing dependency: 'listPets'. Either include it or remove the dependency array
I've tried to include listPets as a dependency but that leads to repeatable call to backend service. What would be the best way to rewrite the call to useApi hook?
Thanks
Change component's useEffect:
useEffect(() => {
listPets()
.then(result => setPetsData(result.data))
.catch(console.log)
}, [listPets]);
And then try to wrap listPets function with useCallBack hook like this:
const showPetById = useCallback((petId) => {
setIsLoading(true);
return petsApi.showPetById(petId, {
headers: {
Authorization: `Bearer ${keycloak.token}`
}
}).finally(() => setIsLoading(false));
},[petsApi]);
you have forget to add listPets into useEffect
useEffect(() => {
listPets()
.then(result => setPetsData(result.data))
.catch(console.log)
}, [listPets]);
I'm using React useEffect hook for getting data and display the loading indicator but my loading is not working.
Heres the useEffect Hook code:
useEffect(() => {
fetchEvents();
}, []);
fetchEvents function code:
const fetchEvents = () => {
setLoading(true);
const requestBody = {
query: `
query {
events {
_id
title
description
price
date
creator {
_id
email
}
}
}
`
};
fetch("http://localhost:5000/graphql", {
headers: {
"Content-Type": "application/json"
},
method: "POST",
body: JSON.stringify(requestBody)
})
.then(res => {
if (res.status !== 200 && res.status !== 201) {
throw new Error("Failed");
}
return res.json();
})
.then(resData => {
const events = resData.data.events;
setEvents(events);
setLoading(false);
})
.catch(err => {
console.log(err);
setLoading(false);
});
};
You should give more info but here an example for you:
import React, { useState, useEffect } from 'react';
import { Spinner } from 'react-bootstrap';
const MyComponent = () => {
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState([]);
useEffect(() => {
setIsLoading(true);
fetch('/data/endpoint')
.then((res) => res.json)
.then((response) => {
setData([...response]);
setIsLoading(false);
});
}, []);
return isLoading ? (
<Spinner />
) : (
<ol>
data.map(items => <li>{items.label}</li>);
</ol>
);
};
The first parameter of useEffect (the function) is only called if one object of the second parameter (the list) is modified. If the list is empty, it never happens.
You could remove the second parameter to apply the function fetchEvents at each update or you could use any constant to run fetchEvents only once.