class FreeApi extends PureComponent {
state = { errored: false, apis: [] };
}
async componentDidMount() {
try {
const rawData = await fetch(FREE_API_URL);
const { entries } = await rawData.json();
this.setState({ errored:false, apis: entries });
} catch (e) {
this.setState({ errored: true, apis: [] });
}
}
render() {
const { apis, errored } = this.state;
return errored
? (<div>Something went wrong</div>)
: <ul>{apis.map(({ API, Link }) => (
<li key={Link}>
{API}::{Link}
</li>
))}
</ul>;
}
import { PureComponent, useEffect, useState } from "react";
const useFreeApi = () => {
const [data, setApi] = useState({ errored: false, apis: [] });
return data;
};
useEffect(() => {
async function updateApi() {
try {
const rawData = await fetch(FREE_API_URL);
const { entries } = await rawData.json();
setApi({ errored: false, apis: entries });
} catch (error) {
setApi({ errored: true, apis: [] });
}
}
updateApi();
}, []);
If you will call fetch directly - the request will be issued every re-render
Don't forget to set an empty array of dependencies to fire useEffect only once
const FreeApi = () => {
const { errored, apis } = useFreeApi();
return errored
? (<div>Something went wrong</div>)
: (<ul>
{apis.map(({ API, Link }) => (
<li key={Link}>
{API}::{Link}
</li>
))}
</ul>);
};
Despite Internet Explorer being deprecated even by the author, it's still popular in a big companies
Fetch doesn't work in IE
Sometimes you will need to cancel a request
Fetch doesn't cancel
The work on canceling is ongoing
Fetch is a bit limited
const axios = Axios.create();
...
useEffect(() => {
async function updateApi() {
try {
const { data } = await axios.get(FREE_API_URL);
const { entries } = data;
setApi({ errored: false, apis: entries });
} catch (error) {
setApi({ errored: true, apis: [] });
}
}
updateApi();
}, []);
const axios = Axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
const abort = new AbortController();
useEffect(()=>{
try {
const { data } = await axios.get(FREE_API_URL,
{ signal: abort.signal });
}
...
return ()=> abort.abort()
}, [])
You can use AbortController or legacy CancelToken
const axios = Axios.create();
axios.interceptors.response.use((response) => {
console.log(response.request.responseURL);
return response;
});
Feel free to use any tool that feet requirements, just ensure it's stable and supported
There is one big fail in the examples above - the component knows what data it needs (OK), how to get them (not OK) and how to put them to the store (double not OK)
The store should be responsible about this stuff
type ApiData = {
status: "pending" | "failed" | "success";
entries: []
};
class Store {
data: ApiData = { status: "pending", entries: [] };
constructor() { makeAutoObservable(this); }
}
fetchApi() {
const abort = new AbortController();
axios.get(FREE_API_URL, { signal: abort.signal })
.then(({ data }) => {
runInAction(() => (
this.data = { status: 'success', entries: data.entries })
);
})
.catch(() => {
runInAction(() => (
this.data = { status: 'failed', entries: [] })
);
});
return abort;
}
const AppApp = observer(() => {
useEffect(() => {
const request = apiStore.fetchApi();
return () => request.abort();
}, []);
const { status } = apiStore.data;
if (status ==='pending') { return <>'pending'</> }
if (status === 'failed') { return <>'failed'</> }
return <ApiList/>
});
If something might fail - it will fail in the most inconvenient time
Start designing your component from failed point
Your backend is not something super stable - it might be slow because of many reasons
Showing some kind of indication that response is awaited - might be a good idea
Users will not always wait till the end of the call, they might just change the page
Always provide a way to cancel the network call
// this will not work
useEffect(async () => {
await fetch(SOME_URL)
}, []);
// but this will
useEffect(() => {
async function myFetch() {
await fetch(SOME_URL);
}
myFetch()
}, []);
const myApp = () => {
const [data, state] = useState({});
// calls BE each render
fetch(SOME_URL).then(setState)
return <></>
}
const myApp = () => {
const [data, setState] = useState({});
useEffect(() => {
// calls BE each render
fetch(SOME_URL).then(setState);
});
return <></>;
};