在 React 中使用 Redux
在上一篇介绍 Redux 使用的文章中我们着重介绍了 abccc
这 5 个核心函数。本文在此基础之上介绍如何在 React 项目中使用 Redux。
在 React 项目中使用 Redux 的时候,通常会涉及到以下几个主要的库:
- redux: 这是 Redux 的核心库,提供了创建 store、分发 action 以及处理 reducer 的基础功能。
- react-redux: 这个库提供了 Redux 和 React 之间的绑定,使得在 React 组件中能够方便地使用 Redux store 中的状态。
安装相关库
npm install redux react-redux
# 或者
yarn add redux react-redux
创建 Redux store
使用 Redux,您首先需要创建一个 Redux store,这是应用全局状态的“容器”。以下是一个简单的 store 设置示例:
import { createStore } from 'redux';
import rootReducer from './reducers'; // 假设您有一个根 reducer
const store = createStore(rootReducer);
export default store;
1. 在函数式组件中使用 Redux
在函数式组件中使用 Redux,通常涉及到以下几个步骤:
- 连接 Redux store: 使用
react-redux
提供的useSelector
和useDispatch
钩子来连接 Redux store。 - 选择状态: 使用
useSelector
钩子从 store 中选择您需要的状态。 - 分发 action: 使用
useDispatch
钩子来分发 action,从而更新 store 中的状态。
1.1 推荐的项目文件结构
如果项目中使用 Redux 作为状态管理器,那么一个推荐的文件组织结构如下所示:
src/
|-- actions/
| |-- index.js
|-- reducers/
| |-- index.js
| |-- someReducer.js
|-- components/
| |-- SomeComponent.js
|-- store/
| |-- index.js
|-- App.js
上述的文件组织结构有助于保持项目的清晰和可维护性:
actions
文件夹包含所有action creators,它们是用于创建并分发actions的函数。reducers
文件夹包含所有的reducer函数,这些函数根据传入的actions来更新应用的状态。components
文件夹包含所有的React组件,这些组件负责渲染UI并可能与Redux store进行交互。store
文件夹包含创建Redux store的逻辑,这是应用全局状态的“容器”。
当然使用 shell 脚本创建是非常便捷的:
#!/bin/bash
# 定义源文件目录
SRC_DIR="src"
# 创建目录和文件
mkdir -p "$SRC_DIR/actions"
mkdir -p "$SRC_DIR/reducers"
mkdir -p "$SRC_DIR/components"
mkdir -p "$SRC_DIR/store"
# 创建文件,如果文件不存在的话
touch "$SRC_DIR/actions/index.js"
touch "$SRC_DIR/reducers/index.js"
if [ ! -f "$SRC_DIR/reducers/someReducer.js" ]; then
touch "$SRC_DIR/reducers/someReducer.js"
fi
touch "$SRC_DIR/components/SomeComponent.js"
touch "$SRC_DIR/store/index.js"
通过这种结构,可以轻松地找到和管理与状态管理相关的所有代码,从而提高代码的可读性和可维护性。同时,这种结构也遵循了关注点分离的原则,使得不同类型的代码(如actions、reducers、components等)被组织在不同的文件夹中,便于开发和维护。
除此之外,推荐使用如下的顺序构建 Redux 数据流:
actions/index.js -> reducers/someReducer.js -> reducers/index.js -> store/index.js -> App.js -> components/SomeComponent.js
1.2 代码示例
actions/index.js: 构建 type 枚举和 actionCreators
export const SOME_ACTION = 'SOME_ACTION';
export function someAction(payload) {
return { type: SOME_ACTION, payload };
}
reducers/someReducer.js: 单个 reducer , 三大构成件 type initialState switch
import { SOME_ACTION } from '../actions';
const initialState = { data: null };
export function someReducer(state = initialState, action) {
switch (action.type) {
case SOME_ACTION:
return { ...state, data: action.payload };
default:
return state;
}
}
reducers/index.js:combine reducer -> rootReducer
import { combineReducers } from 'redux';
import someReducer from './someReducer';
const rootReducer = combineReducers({
someReducer
});
export default rootReducer;
store/index.js: 核心步骤,创建 store
import { createStore } from 'redux';
import rootReducer from './reducers'; // 假设您有一个根 reducer
const store = createStore(rootReducer);
export default store;
App.js:顶层注入
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import SomeComponent from './components/SomeComponent';
function App() {
return (
<Provider store={store}>
<SomeComponent />
</Provider>
);
}
export default App;
components/SomeComponent.js: 勾取data
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { someAction } from '../actions';
export function SomeComponent() {
const dispatch = useDispatch();
const data = useSelector(state => state.someReducer.data);
const handleClick = () => {
dispatch(someAction('New data'));
};
return (
<div>
<p>{data}</p>
<button onClick={handleClick}>Update Data</button>
</div>
);
}
1.3 代码解释
-
actions/index.js:在这个文件中,我们定义了一个action类型
SOME_ACTION
和一个相应的action creator函数someAction
。SOME_ACTION
是一个字符串常量,用于在reducer中标识特定的action。someAction
函数接收一个payload
参数,并返回一个action对象,这个对象包含type
和payload
两个属性。type
属性用于标识这个action的类型,而payload
属性则包含了这个action关联的数据。 -
reducers/someReducer.js:这个文件定义了一个名为
someReducer
的reducer函数。这个函数接收当前的状态和一个action对象作为参数,并根据action的类型来更新状态。在这个例子中,如果action的类型是SOME_ACTION
,那么reducer会返回一个新的状态,这个状态包含了传入的payload
数据。如果action的类型不是SOME_ACTION
,那么reducer会返回原始的状态。 -
reducers/index.js: 在这个文件中,我们使用Redux的
combineReducers
方法来将我们所有的reducer合并成一个root reducer。这样做的好处是,我们可以很容易地管理多个reducer,并且每个reducer只负责管理应用状态的一部分。在这个例子中,我们只有一个reducer,即someReducer
,但我们仍然使用combineReducers
来组织它,以便将来能够轻松地添加更多的reducer。 -
store/index.js:在这个文件中,我们使用核心函数
createStore
结合 rootReducer 创建根 store 并导出。 -
App.js: 在这个文件中,我们引入了
react
,react-redux
的Provider
,我们创建的store
,以及我们的SomeComponent
组件。Provider
是一个Redux组件,它使得Redux store能够通过React的context API在应用组件树中的任何层级被访问。我们将store
作为Provider
的属性,从而将整个应用包裹起来,使得在应用中的任何组件都能够访问到这个store
。 -
components/SomeComponent.js:在这个React组件文件中,我们使用
useSelector
和useDispatch
这两个React-Redux钩子来连接Redux store。useSelector
钩子用于从store中选择我们需要的状态片段,而useDispatch
钩子则用于分发actions。在组件内部,我们定义了一个handleClick
函数,这个函数会在用户点击按钮时被调用,并分发一个someAction
action,从而更新store中的状态。组件的渲染部分会根据当前的状态来显示数据和一个按钮,用户可以通过点击这个按钮来触发状态的更新。
2. 在类组件中使用 Redux
在类组件中使用 Redux,通常涉及到以下几个步骤:
- 连接 Redux store: 使用
react-redux
提供的connect
函数来连接 Redux store。 - 选择状态: 在
mapStateToProps
函数中从 store 中选择需要的状态。 - 分发 action: 在
mapDispatchToProps
函数中定义分发 action 的方法,从而更新 store 中的状态。
2.1 推荐的项目文件结构
对于使用类组件和 Redux 的项目,推荐的文件组织结构仍然与函数式组件相似:
src/
|-- actions/
| |-- index.js
|-- reducers/
| |-- index.js
| |-- someReducer.js
|-- components/
| |-- SomeComponent.js
|-- store/
| |-- index.js
|-- App.js
保持相同的文件结构有助于项目的统一性和可维护性。
2.2 代码示例
actions/index.js 和 reducers/ 文件夹中的代码与函数式组件中的示例相同,因此不再重复。
store/index.js: 创建 store 的代码也保持不变。
App.js:仍然需要在顶层使用 Provider
来包裹应用。
// ...其他引入
import { Provider } from 'react-redux';
import store from './store';
import SomeComponent from './components/SomeComponent';
class App extends React.Component {
render() {
return (
<Provider store={store}>
<SomeComponent />
</Provider>
);
}
}
export default App;
components/SomeComponent.js: 使用 connect
函数连接 Redux store。
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { someAction } from '../actions';
class SomeComponent extends Component {
handleClick = () => {
this.props.dispatchSomeAction('New data');
};
render() {
return (
<div>
<p>{this.props.data}</p>
<button onClick={this.handleClick}>Update Data</button>
</div>
);
}
}
const mapStateToProps = state => ({
data: state.someReducer.data
});
const mapDispatchToProps = dispatch => ({
dispatchSomeAction: payload => dispatch(someAction(payload))
});
export default connect(mapStateToProps, mapDispatchToProps)(SomeComponent);
2.3 代码解释
components/SomeComponent.js:在这个类组件文件中,我们使用了connect
函数来连接Redux store。connect
函数接收两个参数:mapStateToProps
和mapDispatchToProps
。mapStateToProps
是一个函数,它接收整个store的状态,并返回一个对象,这个对象的属性会被传递到组件的props中。在这个例子中,我们从store中选择了someReducer.data
状态,并将其作为data
属性传递到组件中。mapDispatchToProps
也是一个函数,它接收一个dispatch
函数作为参数,并返回一个对象,这个对象的属性是函数,这些函数会调用dispatch
来分发actions。在这个例子中,我们定义了一个dispatchSomeAction
函数,它接收一个payload
参数,并分发一个someAction
action。最后,我们使用connect
函数将这两个映射函数应用到我们的组件上,从而创建一个与Redux store连接的组件。
也就是说需要使用 HOC 向原来的组件的 props 中注入两个属性,一个是 data 一个是 dispatchSomeAction , 分别表示对已有数据的引用和修改 state 的方法。可以看出来,类组件使用的是注入的方式而不是 hook 的方式。但是其基本的步骤却是完全相同的!
以上就是本文的所有内容了。本文旨在一站式说明如何在 React 项目中使用 Redux 状态管理器,并提供了文件结构,感兴趣的同学可以参考之。
转载自:https://juejin.cn/post/7367186272848855074