Redux 与 Context API
我确信,如果您偶然发现了这篇文章,那么您一定对 React 或任何基于组件的前端框架有一定的了解。这些框架可以通过两种方式存储数据:组件级状态和应用级状态。仅使用组件级状态非常方便,而且总是更可取。但有时我们需要应用级状态管理。例如,如果您在一个组件中有一个 TodoList,而在其他组件中有一个 TODO 总数以及已完成和未完成 TODO 数量的计数,那么使用应用级状态将是一个更好的选择。如果没有组件级状态,您将需要将 TODO 从一个组件传递到另一个组件。
在 React 中,主要有两种管理状态的方式。一种是 Redux。Redux 不仅可以与 React 一起使用,还可以与其他框架一起使用。
另一方面,Context API 是 React 中内置的应用程序级状态管理。
因此,在本文中,我们将比较 Redux 和 Context API 的工作原理,并找出应该使用哪一个。剧透警告,这取决于你的偏好。
使用 Redux
所需软件包
- 反应
- Redux :用于createStore()、combineReducer()等函数
- React-Redux :包含useDispatch(用于分派动作)和useSelector(用于从全局状态中选择事物)等方法,Provider也是 React-redux 的一部分。
redux 的组件
Reducer:这些函数会传入状态和动作。它们在 switch case 中与action.type配合使用,并返回更新后的状态。为了正常工作,它们可能需要接受有效负载。有时,在创建 store 之前,你需要合并不同的 Reducer(通常在每个 Reducer 的Reducer 文件夹中)。
store:store 是所有数据的中心。它也会传递给提供程序(通常在index.js中创建,但 Reducer 的合并发生在reducer 文件夹中的index.js中)。
provider:基于 React 的组件,以 store 作为参数(通常在index.js中创建)
actions:向调度程序提供/返回有效负载和操作类型的函数,调度程序将调用所需的reducer。(通常在名为 actions.js 的单独文件中创建)
文件夹结构
以下是我使用 Redux 时使用的文件夹结构。这是一个简单的应用,使用一个 Reducer 来统计按钮的点击次数。免责声明:reducer2.js仅用于演示如何组合两个 Reducer,您可能会用到,也可能不会用到。闲话少叙,让我们来看看文件夹结构和相关代码。
-
源码/
- 行动
- index.js [此文件存储我们需要使用调度程序调用的所有操作] 示例:
export const action_a = (data) => {
return {
type: "ACTION_NAME",
//generally action names are written in all caps
payload: data
}
}
-
减速器
- reducer1.js.示例:
const initialState = 0
export const reducer1 = (state = initialState, action) => {
switch(action){
case 'ACTION_NAME':
return state + payload;
// always return the complete updated set,
// using spread operator will be helpful if you have
// an object in state
default:
return state;
}
}
- reducer2.js
- index.js [for combining all the reducers] example:
import { combineReduce } from "Redux";
import { reducer1 } from "./reducer1";
import { reducer2 } from "./reducer2";
export default megaReducer = combineReducer({
reducer1,
reducer2
});
-
App.js [React App 组件]
-
index.js [React 的主要注入组件。我们将使用它在 React-Redux 包中找到的提供程序将组合的 Reducer 注入到我们的应用中。这里我使用了 Redux DevTools 在控制台中对其进行调试。这是一个 Chrome 扩展程序,可以在这里找到]:
import React from 'react'
import ReactDOM from 'react-dom';
import App from './App'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import megaReducer from './reducers'
const store = createStore(megaReducer,
//this is for devtools-redux, you may or may not use that
window. __REDUX_DEVTOOLS_EXTENSION__
&& window. __REDUX_DEVTOOLS_EXTENSION__ ()
);
ReactDOM.render(
<Provider store = {store}>
<App />
</Provider>,
document.getElementById('root')
);
现在我们唯一需要的就是能够从全局状态访问和更新状态。让我们一步一步来看:
使用 useSelector 访问状态:
useSelector()是React-redux包提供的一个方法,用于从组合的 Reducer 中选择一个Reducer并访问其值。为了演示它的工作原理,我们来编辑App.js文件。
import React from 'react';
import {useSelector} from 'React-redux';
function App(){
const count = useSelector(state => state.reducer1)
return(
<div>
<h1>Number: {{count}}</h1>
</div>
);
}
export default App;
useSelector 函数接受一个回调函数,该回调函数从组合的 Reducer 中返回所需的 Reducer。
使用 useDispatch 更新状态:
之前我们使用useSelector()从组合 Reducer 中选择一个状态。现在我们将了解如何更新状态,因此我们需要再次修改 App.js:
import React from 'react';
import {useSelector, useDispatch} from 'react-redux';
function App(){
const dispatch_control = useDispatch();
const count = useSelector(state => state.reducer1)
return(
<div>
<h1>Number: {{count}}</h1>
</div>
);
}
export default App;
首先,我导入了 useDispatch 函数并将其初始化为 dispatch_control。现在,dispatch_control 将包含useDispatch()返回的函数,该函数最终将用于调度 action。接下来只需导入该 action 并通过 dispatch_control 调用它即可:
import React from 'react';
import {useSelector, useDispatch} from 'react-redux';
import {action_a} from './actions';
function App(){
const dispatch_control = useDispatch();
const count = useSelector(state => state.reducer1)
return(
<div>
<h1>Number: {{count}}</h1>
<button onClick={() => dispatch_control(action_a(1))} >
+1
</button>
</div>
);
}
export default App;
因此,在这里我们将从 ./actions 导入的要分派的操作传递给按钮“+1”的 onClick 事件监听器,并传入有效负载 1,就像之前我们使用带有操作定义和 reducer 操作的有效负载一样。
以上就是 Redux 与 React 结合使用的基本概述。Redux 还有很多内容需要探索,我可能会在另一篇文章中继续探讨。
现在让我们跳转到上下文 API。
使用 Context API
Context API是 React 内置的全局状态管理方式,比Redux更简单
重要的事情
provider:这是一个具有状态的 React 组件,它返回 JSX
context:它是使用名为 createContext() 的函数创建的
Context.js 的结构
import React, {useState, createContext} from 'react'
export const xyzContext = createContext();
export const xyzProvider = (props) => {
const [number, setNumber] = useState(0);
return(
<xyzContext.Provider value = {[number, setNumber]}>
{props.childern}
</xyzContext.Provider>
)
}
在这段代码中,我们创建了一个名为 xyzContext 的新上下文。然后使用 React Hooks 创建了状态。因此,我们导出了两个对象:上下文和提供程序(即 React 组件)。props.children 用于在提供程序组件内部放置组件。
现在只需导入 Provider 并用该组件包装 App。让我们使用 App.js:
import React from 'react';
import { xyzProvider } from './Context'
function App(){
return(
<xyzProvider>
<div>
<h1>Number: </h1>
</div>
</xyzProvider>
);
}
export default App;
现在我们已经用提供程序包装了我们的应用,我们可以使用 React 提供的 context 和useContext()钩子。让我们来渲染我们的数字:
import React from 'react';
import {useContext} from 'react';
import { xyzProvider, xyzContext } from './Context';
function App(){
const [number, setNumber] = useContext(xyzContext);
return(
<xyzProvider>
<div>
<h1>Number: {{number}}</h1>
</div>
</xyzProvider>
);
}
export default App;
哇!现在你可以从全局状态中看到数字了。现在只剩下更新数字了。有了useContext提供的setNumber方法,更新起来就简单多了:
import React from 'react';
import {useContext} from 'react';
import { xyzProvider, xyzContext } from './Context';
function App(){
const [number, setNumber] = useContext(xyzContext);
const increaseNumber = () => {
setNumber(prevNumber => prevNumber + 1);
}
return(
<xyzProvider>
<div>
<h1>Number: {{number}}</h1>
<button onClick="increaseNumber()" >
+1
</button>
</div>
</xyzProvider>
);
}
export default App;
这里我们使用了 onClick 事件监听器来触发increaseNumber函数。在increaseNumber函数中,我们使用了setNumber函数,该函数接受一个函数作为参数。在这个函数中,我们传入之前的状态并返回新的状态。如果您的状态是一个对象,则可以使用扩展运算符
结论
我认为 Context API 相对于 Redux 的主要优势在于,我们无需导入操作并使用它们,而是可以直接在当前组件上操作状态。Context API 也易于设置,并且与 Redux 一样高效。此外,Context API 是内置解决方案,因此您无需担心第三方实现新的更改。因此,我建议使用 Context API 而不是 Redux。但请记住一点:Redux 是行业标准。
文章来源:https://dev.to/ayushmanbthakur/redux-vs-context-api-3182