Redux 数据流和 React 组件生命周期
几乎所有想学习 Redux 的人都见过这张图片。现在对我来说,它非常直观,但在我刚开始学习 Redux 时,它很难理解。为了更好地理解 Redux 中的数据流,在本文中,我将尝试解释 Redux 如何与 React 协同工作。并尝试通过 React Hooks 实现 Redux。
首先,让我们从 Redux 开始。
Redux 是一个状态管理系统。因此,我们需要:
- 拯救国家的地方
- 获取状态的方法
- 改变状态的方法
这就是我们在使用 Redux 时所做的事情:
1.store
是我们保存状态的地方
import { createStore } from "redux";
import { reducer } from "./reduxModule";
const store = createStore(reducer);
2.getState
是获取状态的方法
const state = store.getState();
3. action
&reducer
是改变 mapStateToProps 的方法
const INCREMENT = "redux/increment";
const initialState = {
counter: 0,
};
export const reducer = (state = initialState, action) => {
switch (action.type) {
case INCREMENT:
return {
counter: state.counter + action.amount
};
default:
return state;
}
};
export const incrementAction = (amount = 1) => {
return {
type: INCREMENT,
amount,
};
};
我们需要进一步解释的部分是action
和reducer
。Redux通过和
更新状态。告诉它想要做什么。然后根据 提供的类型和附加数据更新状态。action
reducer
action
reducer
reducer
action
为什么要使用action
和reducer
?
我曾与很多人讨论过为什么他们在项目中使用 Redux。几乎每次答案都是 - “易于在组件之间共享 props 并防止prop-drilling ”。我想这是因为回到我们还没有稳定的时代context API
,使用 Redux 共享 props 似乎是一个合理的选择。但在我看来,这不是 Redux 的核心概念。
使用action
和reducer
更新状态可以使其更容易控制。状态只能根据我们定义的操作进行更改。关于如何更改状态的所有逻辑都在中reducer
。这可以使它更容易维护。
这个想法就像finite-state machine
。如果我们想要添加更多状态,
只需声明另一个动作并将逻辑添加到 reducer 中。
如果你有兴趣了解更多关于state machines
。你可以查看Kent C. Dodds 写的这篇文章。
现在,我们可以像这样将 Redux 形象化。
- 在初始阶段,reducer 接收并返回初始状态。因此,我们将在 getState 中获取初始状态 ({counter: 0})。
dispatch
在更新阶段,我们向 reducer发送一个增量动作(在 redux 中,我们称之为),通过我们在 reducer 中定义的 switch 语句,它将返回一个新的状态({counter: 0})。
接下来我们在 React 中应用
当我们想要在 React 中实现 Redux 时,我们还需要三样东西:
- 在 React 中保存存储状态
- 获取 React 组件中的状态
- React 组件中的 dispatch 动作
对于项目 1,react-redux
有一个名为 的组件Provider
可以帮助我们做到这一点。
import { createStore } from "redux";
import { Provider } from "react-redux";
const store = createStore(reducer);
return (
<Provider store={store}>
<Container />
</Provider>
)
对于第 2 项和第 3 项,react-redux
提供另一个 HOC 调用connect
。它会将 state 和 action 转换为组件 props。这样我们就可以在 React 组件中使用它了。
import { connect } from "react-redux";
import { incrementAction } from "./reduxModule";
const mapStateToProps = state => ({ counter: state.counter });
const mapDispatchToProps = { incrementAction };
export default connect(mapStateToProps, mapDispatchToProps)(Comp);
现在,我们的组件已经能够接收状态并执行 action 了。因此,像这样完成我们的组件就很容易了。
import React from "react";
export default function Comp({ counter, incrementAction }) {
function handleIncreaseOne() {
incrementAction(1);
}
function handleIncreaseTen() {
incrementAction(10);
}
return (
<div>
<span>{counter}</span>
<div>
<button onClick={handleIncreaseOne}>+1</button>
<button onClick={handleIncreaseTen}>+10</button>
</div>
</div>
);
}
以下是供您参考的全部代码:https://github.com/oahehc/react-redux-example/tree/basic
将 Redux 集成到 React 后,可视化效果应该是这样的。
通过 React Hooks 实现 Redux
现在我们知道了 Redux 如何帮助我们管理状态,所以我们可以尝试通过 React hooks 应用同样的想法。
(* 这只是一个例子,用来演示 Redux 的基本思想,请不要用它来替换Redux
你React-Redux
的项目。如果你想了解更多关于 Redux 的细节,你可以查看Dan Abramov 创建的这个教程
) 就像我们之前做的那样,我们可以将其拆分成三个项目。
- 保存状态的地方 ->
context API
- 在 React 组件中获取状态的方法 ->
useContext
- 在 React 组件中改变状态的方法 ->
useContext
&useReducer
// @ReduxModule.js : reducer and action
const INCREMENT = "redux/increment";
export function reducer(state, action) {
switch (action.type) {
case INCREMENT:
return state + action.amount;
default:
return state;
}
}
export function incrementActionCreator(dispatch) {
return amount => {
dispatch({
type: INCREMENT,
amount
});
};
}
// @Provider.js : apply context API to save the state
import React, { useReducer } from "react";
import { reducer, incrementActionCreator } from "./ReduxModule";
export const ReduxContext = React.createContext();
const initialState = 0;
function ReduxProvider({ children }) {
const [counter, dispatch] = useReducer(reducer, initialState);
return (
<ReduxContext.Provider
value={{ counter, incrementAction: incrementActionCreator(dispatch) }}
>
{children}
</ReduxContext.Provider>
);
}
export default ReduxProvider;
// @Comp.js : apply useContext to get state and action from Context
import React, { useContext } from "react";
import { ReduxContext } from "./Provider";
export default function Comp() {
const { counter, incrementAction } = useContext(ReduxContext);
function handleIncreaseOne() {
incrementAction(1);
}
function handleIncreaseTen() {
incrementAction(10);
}
return (
<div>
<span>{counter}</span>
<div>
<button onClick={handleIncreaseOne}>+1</button>
<button onClick={handleIncreaseTen}>+10</button>
</div>
</div>
);
}
参考:https ://github.com/oahehc/react-redux-example/tree/custom-redux
当我们通过 React hooks 实现 Redux 时,我们使用useContext
和useReducer
。这将引出 Redux 的核心概念:
- useContext :与多个组件共享状态
- useReducer :通过状态机处理状态
结论
感谢您的阅读。希望本文能让 Redux 更容易理解。如果您有任何问题或反馈,请随时留下您的评论。
--