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]);
Related
I have a filter function which is filtering data with state data, dataCopy and searchValue. Issue is if i don't include the data state than react gives warning and if i do include it it cause infinite loop cause the data array is changing within the useEffect. How can i make so that i don't get that warning.
Filter function
import React, { useEffect, useState } from 'react'
import Header from '../Components/Header/Header'
import Home from '../Components/Home/Home'
import "./Layout.css"
import Spinner from '../Components/Spinner/Spinner'
function Layout() {
// state for data, copy of data and spinner
const [data, setData] = useState([])
const [dataCopy, setDataCopy] = useState([])
// state for search input in Header.js (define in parent )
const [searchValue, setSearchValue] = useState("")
// changing search value
const changeSearchValue = (value) => {
setSearchValue(value)
}
// useEffect for search functionality
useEffect(() => {
const handleSearch = () => {
if (searchValue !== "") {
const searchFilter = data.filter(item =>
!isNaN(searchValue) ? item.expected_annually_bill_amount.toString().includes(searchValue) :
item.dmo_content.Ausgrid.toString().toLowerCase().includes(searchValue.toLowerCase()))
setData(searchFilter)
} else {
setData(dataCopy)
}
}
handleSearch()
}, [searchValue, dataCopy])
// useEffect for getting data from api
useEffect(() => {
// making post request to get the token
axios.post(`${process.env.REACT_APP_BASE_URL}`, { data: "" },
{
headers:
{
'Api-key': `${process.env.REACT_APP_API_KEY}`,
},
})
// after getting to token returning it for callback
.then((response) => {
return response.data.data.token
})
// using the token to call another api for the needed data
.then((tokenIs) => {
axios.post(`${process.env.REACT_APP_DATA_URL}`,
{ "session_id": `${process.env.REACT_APP_SESSION_ID}` },
{
headers:
{
'Api-key': `${process.env.REACT_APP_API_KEY}`,
'Auth-token': tokenIs,
},
})
.then((response) => {
setData(response.data.data.electricity)
setDataCopy(response.data.data.electricity)
setSpinner(false)
})
})
// catiching any error if happens
.catch((err) => {
setSpinner(false)
alert(err)
})
}, [])
return (<>
<div className='layout'>
<Header
changeSearchValue={changeSearchValue}
searchValue={searchValue}
/>
<Home data={data} />
</div>
)
}
export default Layout
Here you can eliminate data dependency by:
useEffect(() => {
const handleSearch = () => {
if (searchValue !== "") {
setData(data => data.filter(item =>
!isNaN(searchValue) ? item.expected_annually_bill_amount.toString().includes(searchValue) :
item.dmo_content.Ausgrid.toString().toLowerCase().includes(searchValue.toLowerCase())))
} else {
setData(dataCopy)
}
}
handleSearch()
}, [searchValue, dataCopy])
You can add the following comment above the dependency array for suppressing the warning
// eslint-disable-next-line react-hooks/exhaustive-deps
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’
I left the code below that I got my data from. More than one request is processed at the time of refreshing the page, the reason may be why, if you can help I would appreciate it. have a nice day.
import React, { useEffect, useState } from "react";
import axios from "axios"
import Cookies from "universal-cookie"
const Entry = React.createContext();
export const EntryProvider = ({ children }) => {
const [post, setPost] = useState();
const cookie = new Cookies()
const token = cookie.get("acsess_token")
const getAll = () => {
axios.defaults.headers.common['Authorization'] = token;
const entry = axios.get("/api/entry/entry", {
headers: {
"Authorization": token
}
})
.then((response) => {
const data = response.data.data
data.map(element => {
setPost(element)
});
setPost(data)
})
.catch((err) => { console.log(err) })
}
useEffect(() => {
getAll()
},[getAll])
return (
<Entry.Provider value={{post}}>
{children}
</Entry.Provider>
);
};
export const userEntry = () => {
return React.useContext(Entry);
};
Instead adding getAll in the array dependency, remove it
useEffect(() => {
getAll()
},[getAll])
Like this:
useEffect(() => {
getAll()
},[])
Why that?
Because the useEffect will be execute it every time the component renders and because of having getAll in the dependency array it will execute it again
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 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.