Working With API in React
In this video you will learn how to work with API from React on real example both with classes and hooks approach.
Let's jump right into it.
So here I have an empty generated create react app. If you don't know how to do that I already made a video on that. I will link it down in the description.
So to fetch data from API we first need an API. I prefer to use json-server package because we can get a real working API in a matter of seconds.
npm install -g json-server
json-server --version
Now we need to create a db.json file in the root of our project. The idea is that it's our database in json format and json-server will generate a real API using this file and modify it after our requests.
db.json
{
"posts": [
{
"id": "1",
"title": "json-server"
},
{
"id": "2",
"title": "axios"
}
]
}
Now we can start our json-server
json-server --watch db.json --port=3004
As create-react-app already took 3000 port we need to set another port for json-server.
Now we can open http://localhost:3004/posts and we see our completely real API.
The next step is to install a library to fetch data. React don't have any functionality for that this is why 2 most popular ways is axios library or fetch which is available by default but a bit verbose and not that optimised to use.
This is why let's install axios package now.
npm install axios
We are completely ready. So let's look on how we can fetch data in React using classes. Let's create a new component for this.
src/App.js
import FetchWithClasses from "./FetchWithClasses";
const App = () => {
return (
<div>
<h1>Hello monsterlessons</h1>;
<FetchWithClasses />
</div>
);
};
export default App;
src/FetchWithClasses.js
import { Component } from "react";
class FetchWithClasses extends Component {
render() {
return <h1>Fetch with classs</h1>;
}
}
export default FetchWithClasses;
So here we create an additional class component FetchWithClasses. Normally we want to fetch some data on initialize of the component and after we get the data render them on the screen. To do this we have componentDidMount function inside every React class. All code inside it will be called on initialize.
componentDidMount() {
console.log("initialized FetchWithClasses");
}
As you can see in browser we are getting console log so it's exactly the correct place to start fetching our API data. Now we need to import axios and get data from the URL.
import axios from 'axios'
...
componentDidMount() {
console.log("initialized FetchWithClasses");
axios.get("http://localhost:3004/posts").then((response) => {
console.log("response", response);
});
}
So here we used axios.get which gives us back a promise. Inside this promise we are getting a response which is our whole request. And this axios code doesn't have anything to do with React to we somehow need to notify React about data that we got.
class FetchWithClasses extends Component {
state = {
posts: [],
};
componentDidMount() {
console.log("initialized FetchWithClasses");
axios.get("http://localhost:3004/posts").then((response) => {
console.log("response", response);
this.setState({ posts: response.data });
});
}
}
So first of all we are defining state object on the top with posts property. Now after we got successful response we can set with setState posts inside our state.
Now we need to render our posts.
class FetchWithClasses extends Component {
...
render() {
return (
<div>
{this.state.posts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
);
}
}
So here we rendered the list of posts from state and as it's an array by default it won't break when we don't have any data.
Let's make things fancy and add loading element.
class FetchWithClasses extends Component {
state = {
posts: [],
isLoading: false,
};
componentDidMount() {
console.log("initialized FetchWithClasses");
this.setState({ isLoading: true });
axios.get("http://localhost:3004/posts").then((response) => {
console.log("response", response);
this.setState({ posts: response.data, isLoading: false });
});
}
render() {
return (
<div>
{this.state.isLoading && <div>Loading...</div>}
{this.state.posts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
);
}
}
Now it looks much better.
Now let's do the same using React hooks. If you don't know classes is older approach to write React components. Now we have hooks which gives lots of possibilities and I make a full "React hooks for beginners series" here on youtube so if you want to learn them deeper don't forget to check them out.
So let's create one more component where we will fetch and render the same using but with hooks.
const FetchWithHooks = () => {
return <div>FetchWithHooks</div>;
};
export default FetchWithHooks;
import FetchWithHooks from "./FetchWithHooks";
const App = () => {
return (
...
<FetchWithHooks />
...
);
};
So our FetchWithHooks is just a stateless component. But now useState hook allows us to store data inside it between renders. Let's create state for isLoading and posts.
import { useState } from "react";
const FetchWithHooks = () => {
const [isLoading, setIsLoading] = useState(false);
const [posts, setPosts] = useState([]);
return <div>FetchWithHooks</div>;
};
So here we created 2 states for isLoading and posts. useState hook gets as a parameter default value and returns array with 2 elements. The first one is a value and a second a setter to change this value. So we did exactly the same like in classes but we created 2 different properties.
Now we want to fetch data on initialize and set them inside our state. For this we have a special hook which is called useEffect. It allows us to do sideeffects and asynchronous operations inside our components.
const FetchWithHooks = () => {
...
useEffect(() => {
console.log("initialized fetch with hooks");
}, []);
};
In useEffect we must provide a function that will happen and as a second parameter we have an array of dependencies. This array is needed to define when this useEffect will be triggered. By default it will happen after every rendering of our component. We don't need that we need to fetch data only once this is why we provide an empty array to specify that we don't have any dependencies. In this case our useEffect will happen only once after first render and this is exactly what we want. Now we can copy the same code about fetching data from classes to our hooks.
useEffect(() => {
console.log("initialized fetch with hooks");
setIsLoading(true);
axios.get("http://localhost:3004/posts").then((response) => {
console.log("response", response);
setPosts(response.data);
setIsLoading(false);
});
}, []);
So here we have the same get from API using axios. The only difference is how we are setting our state. We are using setters that useState hook created for us.
As you can see in browser, we are getting our data from API and we just need to render them. The rendering part we can copy completely from classes.
const FetchWithHooks = () => {
...
return (
<div>
{isLoading && <div>Loading...</div>}
{posts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
);
};
Now everything is rendered and looking exactly like in classes.
Want to conquer your next JavaScript interview? Download my FREE PDF - Pass Your JS Interview with Confidence and start preparing for success today!
📚 Source code of what we've done