调用 API 时使用 useReducer 而不是 useState!

2025-05-24

调用 API 时使用 useReducer 而不是 useState!

大家好!

React 引入 Hooks 已经有一段时间了,我们都爱上了它的模式和易用性。尽管如此,我们中的许多人并没有充分利用 Hooks 提供的所有功能,而这useReducer正是其中之一!由于 useState 是我们最先学习的 Hook,所以我们很少使用useReducer它。因此,在本文中,我将重点介绍useReducer并引导您了解实现 Hooks 的最佳用例。

那么,让我们开始吧!

useReducer 是什么?

useReducer是 React 中用于现代状态管理的另一个钩子。这个概念首先在 Redux 中引入,后来也被 React 所采用。通常,reducer 是一个接受两个参数(stateaction)的函数。根据传入的 action,reducer 将对状态执行一些操作并返回新的更新状态。在 React 中,它也执行类似的状态管理。您可以在React 文档useReducer中详细了解 useReducer。

如何使用它进行API调用?

到现在为止,你一定已经掌握了 useReducer hook 的基本概念。让我们直接深入代码,了解如何使用 useReducer 来提高代码效率,使其比 useState 更高效。

我们先用简单的 useState 来调用 API。它看起来是这样的:

// user component using useState 
const User = () => {
    const [userDetails, setUserdetails] = useState();
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState();

    useEffect(() => {
        setLoading(true);
        const getUsers = async () => {
            let response = await axios.get('/users');
            if (response.status == 200) {
                setUserdetails(response.data);
                setError(false);
                return;
            }
            setError(response.error);
        };

        getUsers();
        setLoading(false);
    });

    return (
        <div>
            {loading ? (
                <p>loading...</p>
            ) : error ? (
                <p>{error}</p>
            ) : (
                <ul>
                    {userDetails.map((user) => (
                        <li key={user.id}>
                            <h1>{user.name}</h1>
                            <p>{user.location}</p>
                        </li>
                    ))}
                </ul>
            )}
        </div>
    );
};

export default User;
Enter fullscreen mode Exit fullscreen mode

这是一个非常基本的 API 调用。在实际场景中,我们需要管理的状态可能远不止这些。首先,我们假设有 3 个状态需要管理,并且它们相互依赖。当我们的应用程序变得更加复杂时,有时我们最终会定义超过 7-8 个状态。在这种情况下,如果我们只使用 useState,那么跟踪所有状态并同步更新它们就会变得非常繁琐。

为了解决所有这些问题,更好的方法是使用 useReducer。让我们看看使用 useReducer 调用相同的 API。

// user component using useReducer
const ACTIONS = {
    CALL_API: 'call-api',
    SUCCESS: 'success',
    ERROR: 'error',
};

const userDetailsReducer = (state, action) => {
    switch (action.type) {
        case ACTIONS.CALL_API: {
            return {
                ...state,
                loading: true,
            };
        }
        case ACTIONS.SUCCESS: {
            return {
                ...state,
                loading: false,
                userDetails: action.data,
            };
        }
        case ACTIONS.ERROR: {
            return {
                ...state,
                loading: false,
                error: action.error,
            };
        }
    }
};

const initialState = {
    userDetails: '',
    loading: false,
    error: null,
};

const User = () => {
    const [state, dispatch] = useReducer(userDetailsReducer, initialState);
    const { userDetails, loading, error } = state;

    useEffect(() => {
        dispatch({ type: ACTIONS.CALL_API });
        const getUsers = async () => {
            let response = await axios.get('/users');
            if (response.status == 200) {
                dispatch({ type: ACTIONS.SUCCESS, data: response.data });
                return;
            }
            dispatch({ type: ACTIONS.ERROR, error: response.error });
        };

        getUsers();
    });

    return (
        <div>
            {loading ? (
                <p>loading...</p>
            ) : error ? (
                <p>{error}</p>
            ) : (
                <ul>
                    {userDetails.map((user) => (
                        <li key={user.id}>
                            <h1>{user.name}</h1>
                            <p>{user.location}</p>
                        </li>
                    ))}
                </ul>
            )}
        </div>
    );
};

export default User;
Enter fullscreen mode Exit fullscreen mode

这里,我们使用一个 dispatch 函数来调用 Reducer。在 Reducer 内部,定义了 switch case 来处理 dispatch 函数提供的操作。上面声明的 actions 对象将确保每次我们将预定义的操作传递给 dispatch 函数时,都会执行相应的操作。您可以跳过此步骤,直接使用字符串。在每个 switch case 中,我们对给定的状态执行操作并返回一个新状态。

我知道你看到这段代码的第一反应是,这看起来很长!但相信我,它更合理。 useReducer hook 接受两个参数:一个 Reducer 函数和一个初始状态。 Reducer 函数将对传入的 state 执行所有状态更新。但这样做有什么好处呢?

  • 状态将根据操作在单个函数中更新,并且依赖于先前的操作。

    当我们将 action 传递给 reducer 时,我们告诉它要对先前的状态执行什么操作。这样,我们可以确保所有状态都与该操作同步,并且几乎不会错过任何状态的更新。

  • 易于管理复杂状态

    由于只有一个函数负责更新状态,因此管理包含数组和对象的复杂状态会更加容易。我们可以使用 Reducer 来有效地处理对象和数组的更新。

  • 易于测试且可预测

    Reducer 是纯函数,根据预定义的操作执行操作。因此,它们没有任何副作用,并且在输入相同参数时会返回相同的值。这使得它们在实现时可预测且易于测试。

何时选择 useReducer 而不是 useState?

useReducers 比 useState 更适合,但并非每次都适用。如果你的用例很简单,它们会给你的代码增加不必要的复杂性。我使用以下几条规则来选择 useReducer 而不是 useState:
1. 如果存在许多相互依赖的状态。2
. 如果状态是一个复杂的对象。

我希望这些规则也能帮助你决定选择哪个状态管理钩子。如果你还有其他选择因素,请在评论区留言。

感谢您阅读这篇文章!希望它能对您有所帮助。如果您喜欢我的文章,也可以在Twitter上联系我,或者请我喝杯咖啡。

继续学习🙌

文章来源:https://dev.to/hey_yogini/usereducer-instead-of-usestate-while-calling-apis-3e1l
PREV
Strapi,另一个用例:使用 Puppeteer 从任何网站构建你自己的 API
NEXT
useContext 实现更好的状态管理!1 2