My useFetch hook is giving me too many re-renders - javascript

I'm trying to abstract my fetch out to a hook in my expo react native app. The fetch must be able to do POST method. I started out by trying to use and then modify the useHook() effect found at https://usehooks-ts.com/react-hook/use-fetch. It caused too many re-renders. So in searching Stack Overflow I found this article that recommended that I do it as a function. Since, I anticipate almost every fetch request is going to be a POST and done with a submit, that looked like a good idea. But it is still giving me too many re-renders. Then I found this article that said to do it with a useCallBack. Still no success...
Here is the hook I'm trying to use:
import { useCallback, useState } from "react";
import { FetchQuery } from "../interfaces/FetchQuery";
interface State<T> {
data?: T;
error: Error | string | null;
fetchData: any;
}
const useFetch2 = <T = unknown,>(): State<T> => {
const [data, setData] = useState<T>();
const [error, setError] = useState<Error | null>(null);
const fetchData = useCallback(async (query: FetchQuery) => {
const { url, options } = query;
if (!url) return;
const response = await fetch(url, options);
const data = await response.json();
if (data.error) {
setData(undefined);
setError(data.error);
} else {
setData(data);
setError(null);
}
}, []);
return { fetchData, data, error };
};
export default useFetch2;
This is the component calling the code (right now it assigns all of the data to the error field so I can validate the data coming back):
export default function AdminLogin() {
// screen controls
const [err, setErr] = useState<Error | string | undefined>();
// form controls
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
// fetch information
// const [fetchQuery, setFetchQuery] = useState<FetchQuery>({});
const { fetchData, data, error } = useFetch2<User>();
if (error) {
setErr(error);
}
if (data?.user) {
setErr(JSON.stringify(data.user));
}
const handleSignIn = async () => {
const options = {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"Cache-control": "no-cache",
},
body: JSON.stringify({ email, password }),
};
const url = BASE_API + "users/login";
// setFetchQuery({ url, options }); <-- attempted to use this with useEffect
fetchData({ url, options });
};
// useEffect(() => {
// fetchData();
// }, [fetchQuery]);
return (
<View style={styles.container}>
<Text style={styles.msg}>Maps can only be edited by Administers.</Text>
<View style={styles.controlGroup}>
<Text style={styles.UnPw}>Email</Text>
<TextInput
style={styles.txInput}
keyboardType="email-address"
placeholder="Email"
value={email}
onChangeText={(value) => setEmail(value)}
/>
</View>
<View style={styles.controlGroup}>
<Text style={styles.UnPw}>Password</Text>
<TextInput
style={styles.txInput}
maxLength={18}
placeholder="Password"
value={password}
onChangeText={(value) => setPassword(value)}
secureTextEntry
/>
</View>
<TouchableOpacity onPress={handleSignIn}>
<Text>Sign in</Text>
</TouchableOpacity>
{/* {status === "loading" && <Text>Loading...</Text>} */}
{err && (
<>
<Text style={styles.errText}>Error:</Text>
<Text style={styles.errText}>{err}</Text>
</>
)}
</View>
);
}
const styles = StyleSheet.create({});
Adding some console.log commands does show that states are continually updating after the fetch command, but I don't understand why nor how to fix it.

You should not update state directly in the component. This may cause the state to be updated on every render, which forces re-render and creates an infinite render loop.
Instead, state should be updated only in callbacks or useEffect.
Changes you have to make are convert following
if (error) {
setErr(error);
}
if (data?.user) {
setErr(JSON.stringify(data.user));
}
to
useEffect(() => {
if (error) {
setErr(error);
}
if (data?.user) {
setErr(JSON.stringify(data.user));
}
}, [error, data?.user])
Or, even better would be to remove err state and convert it to const variable like
// remove following
// const [err, setErr] = useState<Error | string | undefined>();
// ..some code
const { fetchData, data, error } = useFetch2<User>();
// err is not required to be a state
const err = error || JSON.strigify(data?.user);

You should remove this code which causes infinite re-renders:
if (error) {
setErr(error);
}
if (data?.user) {
setErr(JSON.stringify(data.user));
}
You should put it to useEffect, something like this:
useEffect(() => {
setErr(error);
}, [error])
useEffect(() => {
setErr(JSON.stringify(data.user));
}, [data?.user])
Because that code updates state on every render.

Related

React custom hook does not give result of POST

The custome hook post method working fine at the same time the response adding state taking time.
console.log(jsonResult)
shows the response of POST method at the same time responseData shows null
usePostQuery
import { useCallback, useState } from "react";
interface bodyData {
message: string,
author: string
}
const usePostQuery = (url: string, data?: bodyData )=> {
const [responseData, setResponseData] = useState();
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState('');
const callPost = useCallback( async (data: bodyData) => {
try {
setLoading(true);
const response = await fetch(url, {
method: "POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: data.message,
userId: 15
})
});
const jsonResult = await response.json();
console.log('--------jsonResult---------');
console.log(jsonResult)
setResponseData(jsonResult);
} catch (error: any) {
setError(error.message);
} finally {
setLoading(false);
}
},
[url]
);
return { responseData, loading, error, callPost };
};
export default usePostQuery;
const { responseData, loading, error, callPost } = usePostQuery('https://jsonplaceholder.typicode.com/posts')
The responseData is not giving post call response
useEffect(() => {
if (draftMessage && myMessage) {
// submitNewMessage()
console.log("post my message to server");
callPost({
message: myMessage,
author: "Mo"
});
if (loading === false) {
setMyMessage("");
setdraftMessage(false);
console.log("after ", responseData);
}
console.log("responseData ", responseData);
}
}, [draftMessage, myMessage]);
The fetch is successful because the console in side fetch response shows the API response.
There's nothing wrong with your custom hook. The issue is in your effect hook.
It only triggers when its dependencies change, ie draftMessage and myMessage. It does not re-evaluate loading or responseData so will only ever see their states at the time it is triggered.
It's really unclear what you're using the draftMessage state for. Instead, I would simply trigger the callPost in your submit handler...
export default function App() {
const [myMessage, setMyMessage] = useState("");
const { responseData, loading, callPost } = usePostQuery(
"https://jsonplaceholder.typicode.com/posts"
);
const handleMyMessage = (val) => {
setMyMessage(val);
};
const handleSubmit = async (event) => {
event.preventDefault();
await callPost({ message: myMessage, author: "Mo" });
setMyMessage("");
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{loading ? (
<p>Loading...</p>
) : (
<ChatForm
onChange={handleMyMessage}
myMessage={myMessage}
handleSubmit={handleSubmit}
/>
)}
<pre>responseData = {JSON.stringify(responseData, null, 2)}</pre>
</div>
);
}
Your hook controls the loading and responseData states so there's really very little for your components to do.

Why I am getting 'Uncaught Error: Too many re-renders.' while a try to assign a const that a got from custom hook into another const using useState?

I have made a custom hook that takes url and fetches the data in json format. But when I am trying to assign the data into const users using use state, I getting the error :
'Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop'
Here is the component from where I am trying to assign:
import React, { useState } from "react";
import useFetch from "./fetchData";
import Users from "./Users";
const ASSIGN5 = () => {
const [users, setUsers] = useState();
const { data, isLoading, error } = useFetch(
"https://jsonplaceholder.typicode.com/users"
);
setUsers(data);
return (
<div className="container">
<h1 className="">Users Management App</h1>
{isLoading && <p>Loading users...</p>}
{error && <p>{error}</p>}
<Search onHandleSearch={handleSearch} />
{users && <Users users={users} />}
</div>
);
};
export default ASSIGN5;
And here is the useFetch hook:
import React, { useEffect, useState } from "react";
const useFetch = (url) => {
const [data, setData] = useState([]);
const [isloading, setIsloading] = useState(true);
const [error, setError] = useState();
useEffect(() => {
fetch(url)
.then((res) => {
if (!res.ok) {
throw Error("Fetching unsucessful");
} else {
return res.json();
}
})
.then((data) => {
setData(data);
setIsloading(false);
setError(null);
})
.catch((error) => {
setError(error.message);
setIsloading(false);
});
}, [url]);
return { data, isloading, error };
};
export default useFetch;
But it runs fine when I use data directly without assigning but I need to because have to filter the data using functions
I am expecting that the data will assigned to the const users
Don't call state setters unconditionally in the component body or that'll trigger infinite renders.
It appears you don't need the users state at all because it's just an alias of the data array returned by your useFetch hook.
const ASSIGN5 = () => {
const { data, isLoading, error } = useFetch(
"https://jsonplaceholder.typicode.com/users"
);
return (
<div className="container">
<h1 className="">Users Management App</h1>
{isLoading && <p>Loading users...</p>}
{error && <p>{error}</p>}
<Search onHandleSearch={handleSearch} />
{data?.length && <Users users={data} />}
</div>
);
};
If you want to rename it you can use
const { data: users, isLoading, error } = useFetch(...);
// now you can replace `data` with `users`
Search and handleSearch weren't defined but I assume those are in your actual code somewhere.
Components are typically PascalCase, so ASSIGN5 should be Assign5.

Reactjs overwriting component when I try to create a component per item in my list

I have an array of data resembling the following:
data = [{name: 'A', data: 1}, {name: 'B', data: 2}]
I also have code resembling the following:
function ReportComponent({ data }) {
return data.map((datum) => (
<Typography>
{datum.name}: {datum.data}
</Typography>
));
}
which is called in
function ReportBox({ component }) {
const { data } = useFetchHook(component.urls)
// data returns exactly as expected, an array of objects
return (
<Box>
<Typography>
{component.title}
</Typography>
{data !== null && <ReportComponent data={data} />}
</Box>
);
}
My issue is, when I run the application, I only get one output from my data (when I console.log(data) it returns the data I showed above), either
A: 1 OR B:2. I expect there to be both present in the component. Any advice?
---- Update ----
useFetch function
import { useState, useEffect } from 'react';
function useFetch(urls) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
let i = urls.length - 1;
const result = [];
while (i >= 0) {
const abortCont = new AbortController();
console.log(`url ${i}`);
console.log(urls[i]);
fetch(urls[i], { signal: abortCont.signal }, { mode: 'cors' })
.then((res) => {
if (!res.ok) {
console.log('something went wrong with the data fetch');
}
return res.json(); // why?
})
.then((data) => {
result.push(data);
setData(result);
})
.catch((err) => {
if (err.name === 'AbortError') {
console.log('aborted');
} else {
setError(err.message);
}
});
i -= 1;
}
}, [urls]);
// console.log(data);
return { data, error };
}
export default useFetch;
--- Update DashBox ---
mport { Box, Grid, Container, Typography } from '#mui/material';
import ReportBox from './ReportBox';
function DashBox({ components }) {
// console.log(components);
return (
<Grid
item
columns={5}
sx={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-evenly',
alignItems: 'stretch',
marginTop: '20px',
marginLeft: '5px'
}}
>
{components.map((component) => (
<ReportBox component={component} />
))}
</Grid>
);
}
export default DashBox;
--- Update Page ---
export default function Page() {
const optionsFilter= [
'A',
'B',
'C'
];
const [filter, setFilter] = useState('A');
const componentsPage = [
{
title: 'One',
urls: [
`http://localhost:9000/page1?filter=${filter}`,
`http://localhost:9000/page2?filter=${filter}`
]
}
];
const componentsPageGraphs = [
{
title: 'OneGraph',
urls: [
`http://localhost:9000/page1?filter=${filter}`,
`http://localhost:9000/page2?filter=${filter}`
]
}
];
return (
<Page title="Page">
<Container>
<Typography variant="h4" sx={{ mb: 5 }}>
Page
</Typography>
<Container marginBottom="10px">
<Typography marginLeft="5px" variant="h5">
Filters
</Typography>
<Grid
columns={5}
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'stretch',
marginTop: '10px',
marginLeft: '5px',
justifyContent: 'space-evenly'
}}
>
<Grid item sx={{ pr: 5 }}>
<DropDown
options={optionsFilter}
title="Filter Type"
setData={setFilter}
data={filter}
key="one"
/>
</Grid>
</Grid>
</Container>
<br />
<Box
container
sx={{ border: 2 }}
marginLeft="20px"
pr="20px"
pb="20px"
pl="20px"
width="100%"
>
<Typography variant="h3">Page Dashboard</Typography>
<DashBox components={componentsPage} />
</Box>
<Grid container spacing={2} marginTop="20px">
{componentsPageGraphs.map((component) => (
<Grid item xs={6}>
<Typography>{component.title}</Typography>
<LineChart xtype="category" urls={component.urls} />
</Grid>
))}
</Grid>
</Container>
</Page>
);
}
---- Update again with the suggested fetch, unfortunately still overwriting ---
import { useState, useEffect, useRef } from 'react';
const sameContents = (array1, array2) =>
array1.length === array2.length && array1.every((value, index) => value === array2[index]);
function useFetch(urls) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const urlsRef = useRef(null);
if (!urlsRef.current || !sameContents(urlsRef.current, urls)) {
urlsRef.current = urls.slice();
}
useEffect(() => {
const results = [];
if (!urlsRef.current) {
return;
}
const controller = new AbortController();
const { signal } = controller;
Promise.all(
urlsRef.current.map((url) => {
fetch(url, { signal, mode: 'cors' })
.then((res) => {
if (!res.ok) {
console.log('http issue');
}
return res.json();
})
.then((data) => {
if (!signal.aborted) {
results.push(data);
setData(results);
setError(null);
}
})
.catch((error) => {
if (signal.aborted) {
return;
}
setData(null);
setError(error);
});
return () => {
controller.abort();
};
})
);
}, [urlsRef.current]);
return { data, error };
}
export default useFetch;
Stack Snippet:
const {useState, useEffect} = React;
// Fake Typography component
const Typography = ({children}) => <div>{children}</div>;
// Fake Box component
const Box = ({children}) => <div>{children}</div>;
// Fake fetch hook
function useFetchHook(urls) {
const [data, setData] = useState(null);
useEffect(() => {
setTimeout(() => {
setData([
{name: "One", data: "Data for 'One'"},
{name: "Two", data: "Data for 'Two'"},
{name: "Three", data: "Data for 'Three'"},
]);
}, 500);
}, []);
return {data};
}
function ReportComponent({ data }) {
return data.map((datum) => (
<Typography>
{datum.name}: {datum.data}
</Typography>
));
}
function ReportBox({ component }) {
const { data } = useFetchHook(component.urls)
// data returns exactly as expected, an array of objects
return (
<Box>
<Typography>
{component.title}
</Typography>
{data !== null && <ReportComponent data={data} />}
</Box>
);
}
ReactDOM.render(<ReportBox component={{urls: [], title: "Example"}} />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
Your Page component creates a new componentsPage object with new urls arrays in the components every time it renders. Those new urls arrays are ultimately passed to useFetch (aka useFetchHook), where you have this structure:
function useFetch(urls) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
// ...code that fetches and sets `data`/`error`...
}, [urls]);
// console.log(data);
return { data, error };
}
That means that every time the urls parameter changes value (the old value isn't === the new value), it will repeat the fetch and update data or error.
There are various issues with the hook as well, the primary problem being that it does asynchronous work (a series of fetch calls) but doesn't check to be sure that the results its getting aren't outdated (because urls changed). More on that in a moment.
Since the urls arrays are recreated every time, useFetch does the fetches again every time, because no array is ever === any other array, even if they have the same contents:
console.log(["1", "2", "3"] === ["1", "2", "3"]); // false
So you need to:
Have useFetch only start a new series of fetches when the URLs really change. If it's given a new array with the same contents, it shouldn't do a new set of fetches.
useFetch should abort the fetches that are in progress if it's about to get a new set of urls, and shouldn't use the previous results if that's happened.
You seem to have started on #2 by using an AbortController, but nothing every called its abort method, so it didn't do anything.
Here's a version of useFetch that handles both of those things, see the comments:
const sameContents = (array1, array2) => {
return array1.length === array2.length &&
array1.every((value, index) => value === array2[index]);
};
function useFetch(urls) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const urlsRef = useRef(null); // A place to keep the URLs we're handling
if (!urlsRef.current || // Mounting, or
!sameContents(urlsRef.current, urls) // Called after mount with *different* URLs
) {
// Remember these URLs
urlsRef.current = urls.slice();
}
useEffect(() => {
if (!urlsRef.current) {
// Nothing to do
return;
}
// Use the same controller and signal for all the fetches
const controller = new AbortController();
const {signal} = controller;
// Use `Promise.all` to wait for all the fetches to complete (or one
// of them to fail) before setting `data`.
Promise.all(urlsRef.current.map(url =>
// Note: You had `{ mode: "cors" }` on its own as a third argument,
// but it should have been part of the second argument (`fetch`
// only takes two).
fetch(url, {signal, mode: "cors"})
.then(res => {
if (!res.ok) {
// HTTP error
throw new Error(`HTTP error ${res.status}`);
}
// HTTP okay, read the body of the response and parse it
return res.json();
})
))
.then(data => {
// Got all the data. If this set of results isn't out of date,
// set it and clear any previous error
if (!signal.aborted) {
setData(data);
setError(null);
}
})
.catch(error => {
// Do nothing if these results are out of date
if (signal.aborted) {
return;
}
// Clear data, set error
setData(null);
setError(error);
});
// Return a cleanup callback to abort the set of fetches when we get
// new URLs.
return () => {
controller.abort();
};
}, [urlsRef.current]); // <=== Use this instead of `urls`
return { data, error };
}
That's a sketch, I won't be surprised if you need to make small tweaks to it, but it should get you going the right way.

Fetch data with a custom React hook

I'm newbie in React but I'm developing an app which loads some data from the server when user open the app. App.js render this AllEvents.js component:
const AllEvents = function ({ id, go, fetchedUser }) {
const [popout, setPopout] = useState(<ScreenSpinner className="preloader" size="large" />)
const [events, setEvents] = useState([])
const [searchQuery, setSearchQuery] = useState('')
const [pageNumber, setPageNumber] = useState(1)
useEvents(setEvents, setPopout) // get events on the main page
useSearchedEvents(setEvents, setPopout, searchQuery, pageNumber)
// for ajax pagination
const handleSearch = (searchQuery) => {
setSearchQuery(searchQuery)
setPageNumber(1)
}
return(
<Panel id={id}>
<PanelHeader>Events around you</PanelHeader>
<FixedLayout vertical="top">
<Search onChange={handleSearch} />
</FixedLayout>
{popout}
{
<List id="event-list">
{
events.length > 0
?
events.map((event, i) => <EventListItem key={event.id} id={event.id} title={event.title} />)
:
<InfoMessages type="no-events" />
}
</List>
}
</Panel>
)
}
export default AllEvents
useEvents() is a custom hook in EventServerHooks.js file. EventServerHooks is designed for incapsulating different ajax requests. (Like a helper file to make AllEvents.js cleaner) Here it is:
function useEvents(setEvents, setPopout) {
useEffect(() => {
axios.get("https://server.ru/events")
.then(
(response) => {
console.log(response)
console.log(new Date())
setEvents(response.data.data)
setPopout(null)
},
(error) => {
console.log('Error while getting events: ' + error)
}
)
}, [])
return null
}
function useSearchedEvents(setEvents, setPopout, searchQuery, pageNumber) {
useEffect(() => {
setPopout(<ScreenSpinner className="preloader" size="large" />)
let cancel
axios({
method: 'GET',
url: "https://server.ru/events",
params: {q: searchQuery, page: pageNumber},
cancelToken: new axios.CancelToken(c => cancel = c)
}).then(
(response) => {
setEvents(response.data)
setPopout(null)
},
(error) => {
console.log('Error while getting events: ' + error)
}
).catch(
e => {
if (axios.isCancel(e)) return
}
)
return () => cancel()
}, [searchQuery, pageNumber])
return null
}
export { useEvents, useSearchedEvents }
And here is the small component InfoMessages from the first code listing, which display message "No results" if events array is empty:
const InfoMessages = props => {
switch (props.type) {
case 'no-events':
{console.log(new Date())}
return <Div className="no-events">No results :(</Div>
default:
return ''
}
}
export default InfoMessages
So my problem is that events periodically loads and periodically don't after app opened. As you can see in the code I put console log in useEvents() and in InfoMessages so when it's displayed it looks like this:
logs if events are displayed, and the app itself
And if it's not displayed it looks like this: logs if events are not displayed, and the app itself
I must note that data from the server is loaded perfectly in both cases, so I have totally no idea why it behaves differently with the same code. What am I missing?
Do not pass a hook to a custom hook: custom hooks are supposed to be decoupled from a specific component and possibly reused. In addition, your custom hooks return always null and that's wrong. But your code is pretty easy to fix.
In your main component you can fetch data with a custom hook and also get the loading state like this, for example:
function Events () {
const [events, loadingEvents] = useEvents([])
return loadingEvents ? <EventsSpinner /> : <div>{events.map(e => <Event key={e.id} title={e.title} />}</div>
}
In your custom hook you should return the internal state. For example:
function useEvents(initialState) {
const [events, setEvents] = useState(initialState)
const [loading, setLoading] = useState(true)
useEffect(function() {
axios.get("https://server.ru/events")
.then(
(res) => {
setEvents(res.data)
setLoading(false)
}
)
}, [])
return [events, loading]
}
In this example, the custom hook returns an array because we need two values, but you could also return an object with two key/value pairs. Or a simple variable (for example only the events array, if you didn't want the loading state), then use it like this:
const events = useEvents([])
This is another example that you can use, creating a custom hook that performs the task of fetching the information
export const useFetch = (_url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(true);
useEffect(function() {
setLoading('procesando...');
setData(null);
setError(null);
const source = axios.CancelToken.source();
setTimeout( () => {
axios.get( _url,{cancelToken: source.token})
.then(
(res) => {
setLoading(false);
console.log(res.data);
//setData(res);
res.data && setData(res.data);
// res.content && setData(res.content);
})
.catch(err =>{
setLoading(false);
setError('si un error ocurre...');
})
},1000)
return ()=>{
source.cancel();
}
}, [_url])

Confused about useEffect

I'm building my first Custom React Hook and am confused about what I think is a simple aspect of the code:
export const useFetch = (url, options) => {
const [data, setData] = useState();
const [loading, setLoading] = useState(true);
const { app } = useContext(AppContext);
console.log('** Inside useFetch: options = ', options);
useEffect(() => {
console.log('**** Inside useEffect: options = ', options);
const fetchData = async function() {
try {
setLoading(true);
const response = await axios.get(url, options);
if (response.status === 200) {
setData(response.data);
}
} catch (error) {
throw error;
} finally {
setLoading(false);
}
};
fetchData();
}, []);
return { loading, data };
};
I pass to useFetch two parameters: A url and a headers object that contains an AWS Cognito authorization key that looks like this: Authorization: eyJraWQiOiJVNW... (shortened for brevity)
When I do this the options object on does exist near within useFetch but within the useEffect construct it is empty. YET the url string is correctly populated in BOTH cases.
This makes no sense to me. Might anyone have an idea why this is occurring?
Below an implementation of your code showing that it works as expected.
The async/await has been converted to a Promise but should have the same behavior.
"Inside use fetch" is outputed 3 times:
on mount (useEffect(()=>..., [])
after first state change (setLoading(true))
after second state change (setLoading(false))
and "Inside use effect" is outputed 1 time on mount (useEffect(()=>..., [])
Since it doesn't work for you this way it could mean that when the component mounts, options is not available yet.
You confirm it when saying that when you put options as a dependency, useEffect is called two times with the first fetch failing (most likely because of options missing).
I'm pretty sure you will find the problem with options in the parents of the component using your custom hook.
const axios = {
get: (url, options) => {
return new Promise(resolve => setTimeout(() => resolve({ status: 200, data: 'Hello World' }), 2000));
}
};
const AppContext = React.createContext({ app: null });
const useFetch = (url, options) => {
const [data, setData] = React.useState();
const [loading, setLoading] = React.useState(true);
const { app } = React.useContext(AppContext);
console.log('** Inside useFetch: options = ', JSON.stringify(options));
React.useEffect(() => {
console.log('**** Inside useEffect: options = ', JSON.stringify(options));
const fetchData = function () {
setLoading(true);
const response = axios.get(url, options)
.then(response => {
if (response.status === 200) {
setData(response.data);
}
setLoading(false);
})
.catch(error => {
setLoading(false);
throw error;
});
};
fetchData();
}, []);
return { loading, data };
};
const App = ({url, options}) => {
const { loading, data } = useFetch(url, options);
return (
<div
style={{
display: 'flex', background: 'red',
fontSize: '20px', fontWeight: 'bold',
justifyContent: 'center', alignItems: 'center',
width: 300, height: 60, margin: 5
}}
>
{loading ? 'Loading...' : data}
</div>
);
};
ReactDOM.render(
<App
url="https://www.dummy-url.com"
options={{ headers: { Authorization: 'eyJraWQiOiJVNW...' } }}
/>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root" />

Categories