Using axios with function components

In this section, we'll implement REST API calls using axios in a function component. We'll refactor the App component we built in the last section:

  1. First, we are going to declare a constant, called defaultPosts that is going to hold the default posts state we'll use a little later. We'll add this after the IPost interface and set this to an empty array:
const defaultPosts: IPost[] = [];
  1. We'll remove the IState interface because the state will be structured as individual pieces of state now.
  2. We'll also remove the previous App class component.
  3. Next, let's start the App function component under the defaultPosts constant:
const App: React.SFC = () => {}
  1. We can now create the state for the posts, error, cancel token, loading flag, and posts being edited:
const App: React.SFC = () => {
const [posts, setPosts]: [IPost[], (posts: IPost[]) => void] = React.useState(defaultPosts);

const [error, setError]: [string, (error: string) => void] = React.useState("");

const cancelToken = axios.CancelToken;
const [cancelTokenSource, setCancelTokenSource]: [CancelTokenSource,(cancelSourceToken: CancelTokenSource) => void] = React.useState(cancelToken.source());

const [loading, setLoading]: [boolean, (loading: boolean) => void] = React.useState(false);

const [editPost, setEditPost]: [IPost, (post: IPost) => void] = React.useState({
body: "",
title: "",
userId: 1
});
}

So, we use the useState function to define and initialize all these pieces of state.

  1. We want to make the REST API call to get the posts when the component has first been mounted. We can use the useEffect function, after the lines where the state is defined, to do this passing of an empty array as the second parameter:
React.useEffect(() => {
// TODO - get posts
}, []);
  1. Let's call the REST API to get the posts in the arrow function:
React.useEffect(() => {
axios
.get<IPost[]>("https://jsonplaceholder.typicode.com/posts", {
cancelToken: cancelTokenSource.token,
headers: {
"Content-Type": "application/json"
},
timeout: 5000
});
}, []);
  1. Let's handle the response and set the post-state along with setting the loading state to false:
React.useEffect(() => {
axios
.get<IPost[]>(...)
.then(response => {
setPosts(response.data);
setLoading(false);
});
}, []);
  1. Let's also handle any errors, setting the error state along with the loading state to false:
React.useEffect(() => {
axios
.get<IPost[]>(...)
.then(...)
.catch(ex => {
const err = axios.isCancel(ex)
? "Request cancelled"
: ex.code === "ECONNABORTED"
? "A timeout has occurred"
: ex.response.status === 404
? "Resource not found"
: "An unexpected error has occurred";
setError(err);
setLoading(false);

});
}, []);
  1. We can move on to the event handlers now.  These are very similar to the class component implementation, with const replacing the private access modifier, as well as this.state and this.setState being replaced by the specific state variables and state setter functions. We'll start with the Cancel button click handler:
const handleCancelClick = () => {
if (cancelTokenSource) {
cancelTokenSource.cancel("User cancelled operation");
}
};
  1. Next, we can add the change handlers for the title and body inputs:
const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEditPost({ ...editPost, title: e.currentTarget.value });
};

const handleBodyChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setEditPost({ ...editPost, body: e.currentTarget.value });
};
  1. The Save button click handler is next:
const handleSaveClick = () => {
if (editPost.id) {
axios
.put<IPost>(
`https://jsonplaceholder.typicode.com/posts/${editPost.id}`,
editPost,
{
headers: {
"Content-Type": "application/json"
}
}
)
.then(() => {
setEditPost({
body: "",
title: "",
userId: 1
});
setPosts(
posts.filter(post => post.id !== editPost.id).concat(editPost)
);
});
} else {
axios
.post<IPost>(
"https://jsonplaceholder.typicode.com/posts",
{
body: editPost.body,
title: editPost.title,
userId: editPost.userId
},
{
headers: {
"Content-Type": "application/json"
}
}
)
.then(response => {
setPosts(posts.concat(response.data));
});
}
};
  1. Let's do the Update button next:
const handleUpdateClick = (post: IPost) => {
setEditPost(post);
};
  1. The last handler is for the Delete button:
const handleDeleteClick = (post: IPost) => {
axios
.delete(`https://jsonplaceholder.typicode.com/posts/${post.id}`)
.then(() => {
setPosts(posts.filter(p => p.id !== post.id));
});
};
  1. Our final task is to implement the return statement. Again, this is very similar to the class component render method, with references to this removed: 
return (
<div className="App">
<div className="post-edit">
<input
type="text"
placeholder="Enter title"
value={editPost.title}
onChange={handleTitleChange}
/>
<textarea
placeholder="Enter body"
value={editPost.body}
onChange={handleBodyChange}
/>
<button onClick={handleSaveClick}>Save</button>
</div>
{loading && <button onClick={handleCancelClick}>Cancel</button>}
<ul className="posts">
{posts.map(post => (
<li key={post.id}>
<h3>{post.title}</h3>
<p>{post.body}</p>
<button onClick={() => handleUpdateClick(post)}>Update</button>
<button onClick={() => handleDeleteClick(post)}>Delete</button>
</li>
))}
</ul>
{error && <p className="error">{error}</p>}
</div>
);

That's it! Our function component that interacts with a REST API is complete. If we try this, it should behave exactly as it did before.

The main difference in terms of REST API interaction is that we use the useEffect function to make a REST API call to get data that needs to be rendered. We still do this when the component has been mounted, like we do in class-based components. It's just a different way of tapping into that component life cycle event.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.162.110