使用 useReducer 的 Context API
这是一个演示应用程序,用于展示 Context API 与 useReducer 的协同工作方式🧝🏻♀️
1. 设置你的 Firebase 项目
请编辑./src/firebase.js。
2.yarn start
就是这样!
你好,这里是太史👋
在 React 中如何管理全局数据?
我以前用 Redux,但现在我用 Context API 来实现,而且我甚至没有安装 Redux 和任何 Redux 相关的包!
我认为有两种方法可以实现这件事。
一种简单,一种复杂。
我先解释一下简单的那个☝️
假设我们要管理已登录用户的数据。
首先,我们肯定需要一个 Context 组件。
我在阅读master 分支上的 next.js/userContext.js 文件时发现了这个方法 · vercel/next.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);
如您所见,UserContextComp组件具有user状态变量。
const [user, setUser] = useState(null);
我们将用户数据存储在这个user变量中,并使用函数对其进行更新setUser()。
现在我们需要使用这个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();
现在我们可以在任何地方使用user变量并通过setuser()函数更新它了✌️
useUser从代码中导入函数./src/context/userContext.js并获取所需的变量。
在本例中,我们取loadingUser、user和setUser。
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;
如果您需要更新用户数据,请使用此功能,setUser就像更新普通状态变量一样。
这样,我们就可以使用 useContext 和useReducer hook 了。
我觉得这种方式就像没有 Redux 的 Redux 🤤
当然,Redux 内部也使用了 Context API。
顺便说一下,我在这里做了一个示例应用。
如果您想在本地环境中实现它,请参考一下。
这是一个演示应用程序,用于展示 Context API 与 useReducer 的协同工作方式🧝🏻♀️
请编辑./src/firebase.js。
yarn start就是这样!
总之,让我们开始吧!
./src/context/reducer.js如果你熟悉 Redux,就能轻松理解这一点。
现在我们要定义 reducer 函数和 initialState。默认
值为user。null
// 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;
./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);
./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();
创建一个身份验证组件,并像下面这样使用它App.js。
我们需要登录/注销方法(handleLogin,handleLogout)来处理 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;
正如参考资料所述,useReducer返回state和dispatch。
这是 useState 的替代方案。它接受一个类型为 (state, action) => newState 的 reducer,并返回当前状态以及一个 dispatch 方法。(如果您熟悉 Redux,您应该已经知道它的工作原理。)
这就是为什么我们可以得到这样的变量。
useStateValue()返回useContext(StateContext),并且这将返回useReducer(reducer, initialState)。
const [state, dispatch] = useStateValue();
现在您会看到这样的界面,可以登录/注销。
如果您登录成功,您可以看到您的名字如下所示。
当设置了该值后state.user,将显示您的姓名。
<div>{state.user?.displayName}</div>
这可能看起来有点复杂,但我们可以轻松地理解这个应用在`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