发布于 2026-01-06 1 阅读
0

React、Redux 和 API 第二部分:纯 React(DRY 原则)抽象通用代码 额外内容 接下来

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 获取评论。我们需要复制所有用于处理配置和响应到 Co​​mments 容器的代码。您可以根据需要调用其他任意数量的不同端点,重复上述步骤。

另一种方法是将公共代码抽象出来。例如,我们创建一个新文件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