React、Redux 和 API 第二部分:纯 React 编写(DRY 原则)
抽象通用代码
奖金
接下来
在本系列的第一篇文章中,我展示了如何在 React 中与 API 进行交互。这种方法的主要问题之一是,如果有多个容器需要与 API 通信,那么就会出现大量重复代码。
在这篇文章中,我们将探讨如何使用 React 以 DRY(Don't Repeat Yourself,不要重复自己)的方式与 API 进行交互。
抽象通用代码
让我们回顾一下第一篇文章中的例子:
// Posts.js
import React, { Component } from "react";
import PostList from "./PostList";
class Posts extends Component {
state = {
posts: []
}
async componentDidMount() {
const fetchConfig = {
method: "GET",
headers: new Headers({ "Content-Type": "application/json" }),
mode: "cors"
}
const response = await fetch("https://jsonplaceholder.typicode.com/posts/", fetchConfig);
if (response.ok) {
const posts = await response.json();
this.setState({ posts });
} else {
console.log("error!", error);
}
}
render() {
const { posts } = this.state;
return (
<PostList posts={posts} />
)
}
}
现在,假设我们还想从同一个 API 获取评论。我们需要复制所有用于处理配置和响应到 Comments 容器的代码。您可以根据需要调用其他任意数量的不同端点,重复上述步骤。
另一种方法是将公共代码抽象出来。例如,我们创建一个新文件apiHelper.js:
// apiHelper.js
export const SUCCESSFUL_STATUS = "success";
export const FAILED_STATUS = "failed";
const apiHelper = async ({ method, endpoint }) => {
const fetchConfig = {
method,
headers: new Headers({ "Content-Type": "application/json" }),
mode: "cors"
}
const response = await fetch(`https://jsonplaceholder.typicode.com/${endpoint}/`, fetchConfig);
if (response.ok) {
try {
const data = await response.json();
return {
status: SUCCESSFUL_STATUS,
data
}
} catch (error) {
return {
status: FAILED_STATUS,
error
}
}
} else {
return {
status: FAILED_STATUS
}
}
}
export default apiHelper;
在这里,我们将所有处理操作从 PostList 移到了辅助函数中,并使其接受一些参数。
现在来看看帖子和评论的显示效果:
// Posts.js
import React, { Component } from "react";
import apiHelper, { SUCCESSFUL_STATUS } from "../utils/apiHelper";
import PostList from "./PostList";
class Posts extends Component {
state = {
posts: []
}
componentDidMount() {
const { status, data } = apiHelper({ method: "GET", endpoint: "posts" });
if (status === SUCCESSFUL_STATUS) {
this.setState(() => ({ posts: data }));
}
}
render() {
const { posts } = this.state;
return (
<PostList posts={posts} />
)
}
}
// Comments.js
import React, { Component } from "react";
import apiHelper, { SUCCESSFUL_STATUS } from "../utils/apiHelper";
import CommentList from "./CommentList";
class Comments extends Component {
state = {
comments: []
}
componentDidMount() {
const { status, data } = apiHelper({ method: "GET", endpoint: "comments" });
if (status === SUCCESSFUL_STATUS) {
this.setState(() => ({ comments: data }));
}
}
render() {
const { comments } = this.state;
return (
<CommentList comments={comments} />
)
}
}
如您所见,只需稍作调整即可使其更加灵活,而无需重复上述内容。
奖金
如果你想与多个 API 交互,但又想尽量减少代码重复,该怎么办?以下是一个重构示例,展示了如何apiHelper.js实现这一目标:
// apiHelper.js
export const SUCCESSFUL_STATUS = "success";
export const FAILED_STATUS = "failed";
const buildAPIHelper = (args) => async ({ method, endpoint }) => {
const {
baseURL,
headers = new Headers({ "Content-Type": "application/json" }) // some sane defaults
} = args;
const fetchConfig = {
method,
headers,
mode: "cors"
}
const response = await fetch(`${baseURL}${endpoint}`, fetchConfig);
if (response.ok) {
try {
const data = await response.json();
return {
status: SUCCESSFUL_STATUS,
data
}
} catch (error) {
return {
status: FAILED_STATUS,
error
}
}
} else {
return {
status: FAILED_STATUS
}
}
}
export const firstAPIHelper = buildAPIHelper({
baseURL: "https://jsonplaceholder.typicode.com/",
});
export const secondAPIHelper = buildAPIHelper({
baseURL: "https://api.patrick-gordon.com/"
headers: new Headers({ "Content-Type": "application/json", "Authorization": "bearer someKey" })
});
接下来
在本系列的下一部分中,我们将引入 Redux,并探讨如何使用 Redux 与 API 进行通信。
在此之前,祝好!
——帕特里克
文章来源:https://dev.to/patrickgordon/react-redux-and-apis-part-two-react-only-dry-58jg