在 React 中使用 Redux

在上一篇介绍 Redux 使用的文章中我们着重介绍了 abccc 这 5 个核心函数。本文在此基础之上介绍如何在 React 项目中使用 Redux。

传送门:Redux 前菜 -- 理解 Redux 中的五个核心函数 - 掘金 (juejin.cn)

在 React 项目中使用 Redux 的时候,通常会涉及到以下几个主要的库:

  1. redux: 这是 Redux 的核心库,提供了创建 store、分发 action 以及处理 reducer 的基础功能。
  2. react-redux: 这个库提供了 Redux 和 React 之间的绑定,使得在 React 组件中能够方便地使用 Redux store 中的状态。

安装相关库

bash 复制代码
npm install redux react-redux
# 或者
yarn add redux react-redux

创建 Redux store

使用 Redux,您首先需要创建一个 Redux store,这是应用全局状态的"容器"。以下是一个简单的 store 设置示例:

javascript 复制代码
import { createStore } from 'redux';
import rootReducer from './reducers'; // 假设您有一个根 reducer

const store = createStore(rootReducer);

export default store;

1. 在函数式组件中使用 Redux

在函数式组件中使用 Redux,通常涉及到以下几个步骤:

  1. 连接 Redux store : 使用 react-redux 提供的 useSelectoruseDispatch 钩子来连接 Redux store。
  2. 选择状态 : 使用 useSelector 钩子从 store 中选择您需要的状态。
  3. 分发 action : 使用 useDispatch 钩子来分发 action,从而更新 store 中的状态。

1.1 推荐的项目文件结构

如果项目中使用 Redux 作为状态管理器,那么一个推荐的文件组织结构如下所示:

plaintext 复制代码
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的逻辑,这是应用全局状态的"容器"。

通过这种结构,可以轻松地找到和管理与状态管理相关的所有代码,从而提高代码的可读性和可维护性。同时,这种结构也遵循了关注点分离的原则,使得不同类型的代码(如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

javascript 复制代码
export const SOME_ACTION = 'SOME_ACTION';
export function someAction(payload) {
  return { type: SOME_ACTION, payload };
}

reducers/someReducer.js: 单个 reducer , 三大构成件 type initialState switch

javascript 复制代码
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

javascript 复制代码
import { combineReducers } from 'redux';
import someReducer from './someReducer';

const rootReducer = combineReducers({
  someReducer
});

export default rootReducer;

store/index.js: 核心步骤,创建 store

javascript 复制代码
import { createStore } from 'redux';
import rootReducer from './reducers'; // 假设您有一个根 reducer

const store = createStore(rootReducer);

export default store;

App.js:顶层注入

javascript 复制代码
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

javascript 复制代码
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 代码解释

  1. actions/index.js :在这个文件中,我们定义了一个action类型SOME_ACTION和一个相应的action creator函数someActionSOME_ACTION是一个字符串常量,用于在reducer中标识特定的action。someAction函数接收一个payload参数,并返回一个action对象,这个对象包含typepayload两个属性。type属性用于标识这个action的类型,而payload属性则包含了这个action关联的数据。

  2. reducers/someReducer.js :这个文件定义了一个名为someReducer的reducer函数。这个函数接收当前的状态和一个action对象作为参数,并根据action的类型来更新状态。在这个例子中,如果action的类型是SOME_ACTION,那么reducer会返回一个新的状态,这个状态包含了传入的payload数据。如果action的类型不是SOME_ACTION,那么reducer会返回原始的状态。

  3. reducers/index.js : 在这个文件中,我们使用Redux的combineReducers方法来将我们所有的reducer合并成一个root reducer。这样做的好处是,我们可以很容易地管理多个reducer,并且每个reducer只负责管理应用状态的一部分。在这个例子中,我们只有一个reducer,即someReducer,但我们仍然使用combineReducers来组织它,以便将来能够轻松地添加更多的reducer。

  4. store/index.js :在这个文件中,我们使用核心函数 createStore 结合 rootReducer 创建根 store 并导出。

  5. App.js : 在这个文件中,我们引入了react, react-reduxProvider,我们创建的store,以及我们的SomeComponent组件。Provider是一个Redux组件,它使得Redux store能够通过React的context API在应用组件树中的任何层级被访问。我们将store作为Provider的属性,从而将整个应用包裹起来,使得在应用中的任何组件都能够访问到这个store

  6. components/SomeComponent.js :在这个React组件文件中,我们使用useSelectoruseDispatch这两个React-Redux钩子来连接Redux store。useSelector钩子用于从store中选择我们需要的状态片段,而useDispatch钩子则用于分发actions。在组件内部,我们定义了一个handleClick函数,这个函数会在用户点击按钮时被调用,并分发一个someAction action,从而更新store中的状态。组件的渲染部分会根据当前的状态来显示数据和一个按钮,用户可以通过点击这个按钮来触发状态的更新。

2. 在类组件中使用 Redux

在类组件中使用 Redux,通常涉及到以下几个步骤:

  1. 连接 Redux store : 使用 react-redux 提供的 connect 函数来连接 Redux store。
  2. 选择状态 : 在 mapStateToProps 函数中从 store 中选择需要的状态。
  3. 分发 action : 在 mapDispatchToProps 函数中定义分发 action 的方法,从而更新 store 中的状态。

2.1 推荐的项目文件结构

对于使用类组件和 Redux 的项目,推荐的文件组织结构仍然与函数式组件相似:

plaintext 复制代码
src/
|-- actions/
|   |-- index.js
|-- reducers/
|   |-- index.js
|   |-- someReducer.js
|-- components/
|   |-- SomeComponent.js
|-- store/
|   |-- index.js
|-- App.js

保持相同的文件结构有助于项目的统一性和可维护性。

2.2 代码示例

actions/index.jsreducers/ 文件夹中的代码与函数式组件中的示例相同,因此不再重复。

store/index.js: 创建 store 的代码也保持不变。

App.js :仍然需要在顶层使用 Provider 来包裹应用。

javascript 复制代码
// ...其他引入
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。

javascript 复制代码
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函数接收两个参数:mapStateToPropsmapDispatchToPropsmapStateToProps是一个函数,它接收整个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 状态管理器,并提供了文件结构,感兴趣的同学可以参考之。

相关推荐
zqx_720 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
TonyH20022 天前
webpack 4 的 30 个步骤构建 react 开发环境
前端·css·react.js·webpack·postcss·打包
掘金泥石流2 天前
React v19 的 React Complier 是如何优化 React 组件的,看 AI 是如何回答的
javascript·人工智能·react.js
lucifer3112 天前
深入解析 React 组件封装 —— 从业务需求到性能优化
前端·react.js
秃头女孩y2 天前
React基础-快速梳理
前端·react.js·前端框架
sophie旭2 天前
我要拿捏 react 系列二: React 架构设计
javascript·react.js·前端框架
BHDDGT2 天前
react-问卷星项目(5)
前端·javascript·react.js
liangshanbo12153 天前
将 Intersection Observer 与自定义 React Hook 结合使用
前端·react.js·前端框架
黄毛火烧雪下3 天前
React返回上一个页面,会重新挂载吗
前端·javascript·react.js