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

如何在不使用 Redux 的情况下,通过 React Context API 和 useReducer 管理全局数据?AWS 安全直播!

如何在不使用 Redux 的情况下,通过 React 的 Context API 管理全局数据

使用 useReducer 的 Context API

AWS 安全直播!

你好,这里是太史👋

在 React 中如何管理全局数据?
我以前用 Redux,但现在我用 Context API 来实现,而且我甚至没有安装 Redux 和任何 Redux 相关的包!

使用 Context API 实现它的两种方法

我认为有两种方法可以实现这件事。
一种简单,一种复杂。

我先解释一下简单的那个☝️

假设我们要管理已登录用户的数据。

1. 使用状态变量

首先,我们肯定需要一个 Context 组件。

我在阅读master 分支上的 next.js/userContext.js 文件时发现了这个方法 · vercel/next.js 😋

添加 userContext.js

让我们来制作./src/context/userContext.js

// File: ./src/context/userContext.js
import React, { useState, useEffect, createContext, useContext } from 'react';
import firebase from '../firebase/clientApp';

export const UserContext = createContext();

export default function UserContextComp({ children }) {
  const [user, setUser] = useState(null);
  const [loadingUser, setLoadingUser] = useState(true); // Helpful, to update the UI accordingly.

  useEffect(() => {
    // Listen authenticated user
    const unsubscriber = firebase.auth().onAuthStateChanged(async (user) => {
      try {
        if (user) {
          // User is signed in.
          const { uid, displayName, email, photoURL } = user;
          // You could also look for the user doc in your Firestore (if you have one):
          // const userDoc = await firebase.firestore().doc(`users/${uid}`).get()
          setUser({ uid, displayName, email, photoURL });
        } else setUser(null);
      } catch (error) {
        // Most probably a connection error. Handle appropriately.
      } finally {
        setLoadingUser(false);
      }
    });

    // Unsubscribe auth listener on unmount
    return () => unsubscriber();
  }, []);

  return (
    <UserContext.Provider value={{ user, setUser, loadingUser }}>
      {children}
    </UserContext.Provider>
  );
}

// Custom hook that shorhands the context!
export const useUser = () => useContext(UserContext);
Enter fullscreen mode Exit fullscreen mode

如您所见,UserContextComp组件具有user状态变量。

const [user, setUser] = useState(null);
Enter fullscreen mode Exit fullscreen mode

我们将用户数据存储在这个user变量中,并使用函数对其进行更新setUser()

编辑 index.js

现在我们需要使用这个UserContextComp组件来使用它!请按如下方式
编辑./src/index.js

// File: ./src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import UserProvider from './context/userContext';

ReactDOM.render(
  <React.StrictMode>
    <UserProvider>
      <App />
    </UserProvider>
  </React.StrictMode>,
  document.getElementById('root'),
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
Enter fullscreen mode Exit fullscreen mode

现在我们可以在任何地方使用user变量并通过setuser()函数更新它了✌️

如何食用它

useUser从代码中导入函数./src/context/userContext.js并获取所需的变量。
在本例中,我们取loadingUserusersetUser

import React from 'react';
import { useUser } from '../context/userContext';

const MyComponent = () => {
  const { loadingUser, user, setUser } = useUser();

  return (
    <>
      {loadingUser ? (
        <div>loading…</div>
      ) : (
        <div>Welcome, {user.displayName}</div>
      )}
    </>
  );
};

export default MyComponent;
Enter fullscreen mode Exit fullscreen mode

如果您需要更新用户数据,请使用此功能,setUser就像更新普通状态变量一样。

2. 使用 dispatch 和 reducer(更接近 Redux 的方式)

这样,我们就可以使用 useContext 和useReducer hook 了。

我觉得这种方式就像没有 Redux 的 Redux 🤤
当然,Redux 内部也使用了 Context API。

顺便说一下,我在这里做了一个示例应用。
如果您想在本地环境中实现它,请参考一下。

使用 useReducer 的 Context API

这是一个演示应用程序,用于展示 Context API 与 useReducer 的协同工作方式🧝🏻‍♀️

屏幕截图 2563-10-03 18 04 06

1. 设置你的 Firebase 项目

请编辑./src/firebase.js

2.yarn start

就是这样!






总之,让我们开始吧!

添加./src/context/reducer.js

如果你熟悉 Redux,就能轻松理解这一点。

现在我们要定义 reducer 函数和 initialState。默认
值为usernull

// File: ./src/context/reducer.js
export const initialState = {
  user: null,
};

export const actionTypes = {
  SET_USER: 'SET_USER',
};

const reducer = (state, action) => {
  switch (action.type) {
    case actionTypes.SET_USER:
      return {
        ...state,
        user: action.user,
      };
    default:
      return state;
  }
};

export default reducer;
Enter fullscreen mode Exit fullscreen mode

制作./src/context/StateProvider.js

// File: ./src/context/StateProvider.js`

import React, { createContext, useContext, useReducer } from 'react';

export const StateContext = createContext([]);

export const StateProvider = ({ reducer, initialState, children }) => (
  <StateContext.Provider value={useReducer(reducer, initialState)}>
    {children}
  </StateContext.Provider>
);

export const useStateValue = () => useContext(StateContext);
Enter fullscreen mode Exit fullscreen mode

在“设置提供商”中./src/index.js

正因如此,我们可以在任何地方使用 StateContext 组件!

// File: ./src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
+ import { StateProvider } from './context/StateProvider';
+ import reducer, { initialState } from './context/reducer';

ReactDOM.render(
  <React.StrictMode>
+    <StateProvider initialState={initialState} reducer={reducer}>
      <App />
+    </StateProvider>
  </React.StrictMode>,
  document.getElementById('root'),
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
Enter fullscreen mode Exit fullscreen mode

现在显示已登录用户的姓名!

创建一个身份验证组件,并像下面这样使用它App.js

我们需要登录/注销方法(handleLoginhandleLogout)来处理 onclick 事件,所以也需要创建它们。

// File: ./src/App.js
import React from 'react';

import Auth from './Auth';
import { auth, provider } from './firebase';
import { useStateValue } from './context/StateProvider';
import { actionTypes } from './context/reducer';
import './App.css';

function App() {
  const [state, dispatch] = useStateValue();

  const handleLogin = async () => {
    try {
      const result = await auth.signInWithPopup(provider);
      dispatch({
        type: actionTypes.SET_USER,
        user: result.user,
      });
    } catch (err) {
      alert(err.message);
    }
  };

  const handleLogout = async () => {
    await auth.signOut();
    dispatch({
      type: actionTypes.SET_USER,
      user: null,
    });
  };

  return (
    <Auth>
      <div className="App">
        <header className="App-header">
          <div>{state.user?.displayName}</div>
          {state.user ? (
            <button onClick={handleLogout}>Logout</button>
          ) : (
            <button onClick={handleLogin}>Login</button>
          )}
        </header>
      </div>
    </Auth>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

正如参考资料所述,useReducer返回statedispatch

这是 useState 的替代方案。它接受一个类型为 (state, action) => newState 的 reducer,并返回当前状态以及一个 dispatch 方法。(如果您熟悉 Redux,您应该已经知道它的工作原理。)

这就是为什么我们可以得到这样的变量。

useStateValue()返回useContext(StateContext),并且这将返回useReducer(reducer, initialState)

const [state, dispatch] = useStateValue();
Enter fullscreen mode Exit fullscreen mode

现在您会看到这样的界面,可以登录/注销。

登录

如果您登录成功,您可以看到您的名字如下所示。

当设置了该值后state.user,将显示您的姓名。

<div>{state.user?.displayName}</div>
Enter fullscreen mode Exit fullscreen mode

注销

笔记

这可能看起来有点复杂,但我们可以轻松地理解这个应用在`initialState`2. Use dispatch and reducer (more Redux way)中全局管理的数据类型。在这个例子中,我们只全局管理了一个变量,但想象一下,如果我们要管理 10 个变量会怎么样😅user

希望这能帮到你。

文章来源:https://dev.to/taishi/how-to-manage-global-data-with-context-api-not-redux-on-react-20m7