发布于 2026-01-05 5 阅读
0

如何在 ReactJS 中使用 Redux(附实际示例)

如何在 ReactJS 中使用 Redux(附实际示例)

自从我在Creative-Tim开始使用ReactJS以来,我只用它来创建简单的 React 应用,或者说模板。我一直都只用create-react-app来使用 ReactJS ,从未尝试过将其与其他组件集成。

很多用户问过我和我的团队,我们创建的模板是否使用了Redux,或者它们的设计是否能够与 Redux 兼容。我的回答通常是:“我还没有使用过 Redux,所以不知道该如何回答你。”

所以,我现在正在写一篇关于 Redux 以及如何在 React 中使用 Redux 的文章。稍后,我将在本文中把 Redux 添加到我过去一年多以来参与的一个项目中。

在深入研究这两个库之前,了解这些信息很有帮助:

创建一个新的基于 ReactJS 的项目并向其中添加 Redux。

首先,让我们创建一个新的 React 应用,进入该目录并启动它。

create-react-app react-redux-tutorial
cd react-redux-tutorial
npm start
Enter fullscreen mode Exit fullscreen mode

npm start 命令的默认 create-react-app 输出
|npm start 的 create-react-app 默认输出|

我们可以看到,create-react-app 为我们提供了一个非常基本的模板,其中包含一个段落、一个指向 React 网站的锚点以及官方 ReactJS 图标的旋转效果。

我还没告诉你们我们要用 Redux 做什么,或者我们在这里做什么。这是因为我需要上面那张 GIF 图片。

为了让这篇教程文章简洁易懂,我们不会构建非常复杂的功能。我们将使用 Redux 来实现上述 React 图片的旋转停止或启动。

综上所述,接下来我们将添加以下Redux包:

npm install --save redux react-redux
Enter fullscreen mode Exit fullscreen mode

redux v4.0.1

  • 从广义上讲,Redux 的作用是为整个应用程序创建一个全局状态,任何组件都可以访问该状态。
  • 这是一个状态管理图书馆
  • 你的整个应用只有一个状态,而不是每个组件都有一个状态。

react-redux v5.1.1

  • 这样做是为了让我们能够访问 Redux 的数据,并通过向 Redux 发送 action 来修改它——实际上并不是直接向 Redux 发送 action,但我们稍后会讲到。
  • 官方文档指出:它允许你的 React 组件从 Redux store 读取数据,并向 store 发送 action 来更新数据。

注意如果上述命令出现问题,请尝试单独安装软件包。

使用 Redux 时,您需要三样主要的东西:

  • 操作:这些对象应该有两个属性,一个描述操作的类型,另一个描述应该在应用程序状态中更改什么。
  • reducer:这些函数实现了 action 的行为。它们根据 action 描述和状态变更描述来改变应用程序的状态。
  • store:它将 actions 和 reducers 放在一起,保存和更改整个应用程序的状态——只有一个 store。

正如我上面所说,我们将停止并启动 React 标志的旋转动画。这意味着我们需要执行以下两个操作:

1 — Linux / Mac 命令

mkdir src/actions
touch src/actions/startAction.js
touch src/actions/stopAction.js
Enter fullscreen mode Exit fullscreen mode

2 — Windows 命令

mkdir src\actions
echo "" > src\actions\startAction.js
echo "" > src\actions\stopAction.js
Enter fullscreen mode Exit fullscreen mode

现在让我们按如下方式编辑src/actions/startAction.js文件:

export const startAction = {
  type: "rotate",
  payload: true
};
Enter fullscreen mode Exit fullscreen mode

所以,我们要告诉 reducer,这个 action 的类型是关于 React logo 的旋转(rotate)。React logo 旋转的状态应该改为true——我们希望 logo 开始旋转。

现在让我们按如下方式编辑src/actions/stopAction.js文件:

export const stopAction = {
  type: "rotate",
  payload: false
};
Enter fullscreen mode Exit fullscreen mode

所以,我们要告诉 reducer,这个 action 的类型是关于 React logo 的旋转(rotate)。并且 React logo 旋转状态应该改为false——我们希望 logo 停止旋转。

接下来,我们来为我们的应用创建 reducer:

1 — Linux / Mac 命令

mkdir src/reducers
touch src/reducers/rotateReducer.js
Enter fullscreen mode Exit fullscreen mode

2 — Windows 命令

mkdir src\reducers
echo "" > src\reducers\rotateReducer.js
Enter fullscreen mode Exit fullscreen mode

然后,在其中添加以下代码:

export default (state, action) => {
  switch (action.type) {
    case "rotate":
      return {
        rotating: action.payload
      };
    default:
      return state;
  }
};
Enter fullscreen mode Exit fullscreen mode

因此,reducer 将接收到我们两个 action,它们的类型都是rotate,并且都会改变应用程序中的同一个状态——state.rotating 根据这些 action 的有效负载,state.rotating 的值会变为truefalse

我添加了一个默认情况,如果操作类型不是“旋转” ,则保持状态不变。设置默认值是为了防止我们创建操作后忘记为其添加相应的情况。这样,我们就不会删除整个应用状态——我们什么也不做,只是保留原有状态。

最后一步是为整个应用创建 store。由于整个应用只有一个 store/一个状态,所以我们不会为 store 创建新文件夹。当然,你也可以创建一个新文件夹并将 store 添加到其中,但这与操作不同,操作可以有多个,最好将它们放在同一个文件夹中。

因此,接下来我们将运行以下命令:

1 — Linux / Mac 命令

touch src/store.js
Enter fullscreen mode Exit fullscreen mode

2 — Windows 命令

echo "" > src\store.js
Enter fullscreen mode Exit fullscreen mode

此外,还要在其中添加以下代码:

import { createStore } from "redux";
import rotateReducer from "reducers/rotateReducer";


function configureStore(state = { rotating: true }) {
  return createStore(rotateReducer,state);
}

export default configureStore;
Enter fullscreen mode Exit fullscreen mode

因此,我们创建了一个名为configureStore 的函数,在其中我们传递一个默认状态,然后我们使用创建的 reducer 和默认状态创建我们的 store。

我不确定你是否看过我的导入语句,它们使用了绝对路径,所以你可能会因此遇到一些错误。解决方法有两种:

任何一个

1 — 像这样在你的应用中添加一个 .env 文件:

echo "NODE_PATH=./src" > .env`
Enter fullscreen mode Exit fullscreen mode

或者

2 — 全局安装 cross-env,并按如下方式修改 package.json 文件中的启动脚本:

npm install -g cross-env
Enter fullscreen mode Exit fullscreen mode

在 package.json 文件中

"start": "NODE_PATH=./src react-scripts start",`
Enter fullscreen mode Exit fullscreen mode

现在我们已经设置好了 store、actions 和 reducer,接下来需要在src/App.css文件中添加一个新的类。这个类会暂停 logo 的旋转动画。

所以,我们要在src/App.css文件中写入以下代码:

.App-logo-paused {
  animation-play-state: paused;
}
Enter fullscreen mode Exit fullscreen mode

所以你的App.css文件应该看起来像这样:

.App {
  text-align: center;
}

.App-logo {
  animation: App-logo-spin infinite 20s linear;
  height: 40vmin;
}

/* new class here */
.App-logo-paused {
  animation-play-state: paused;
}

.App-header {
background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.App-link {
  color: #61dafb;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
Enter fullscreen mode Exit fullscreen mode

现在,我们只需要修改src/App.js文件,使其监听应用商店的状态。点击 logo 时,它会调用相应的启动或停止操作。

首先,我们需要将组件连接到 redux store,所以我们从react-redux导入 connect 。

import { connect } from "react-redux";
Enter fullscreen mode Exit fullscreen mode

之后,我们将通过 connect 方法导出我们的 App 组件,如下所示:

export default connect()(App);
Enter fullscreen mode Exit fullscreen mode

要改变 Redux store 的状态,我们需要用到之前执行的操作,所以让我们也导入它们:

import { startAction } from "actions/startAction";
import { stopAction } from "actions/stopAction";
Enter fullscreen mode Exit fullscreen mode

现在我们需要从我们的存储中检索状态,并说明我们希望使用开始和停止操作来改变状态。

这将使用 connect 函数来实现,该函数接受两个参数:

  • mapStateToProps:用于检索 store 状态
  • mapDispatchToProps:用于检索 action 并将其分发到 store。

您可以在这里阅读更多关于它们的信息:react-redux connect 函数参数

所以,让我们在 App.js 文件中(最好放在文件末尾)写入以下代码:

const mapStateToProps = state => ({
  ...state
});

const mapDispatchToProps = dispatch => ({
  startAction: () => dispatch(startAction),
  stopAction: () => dispatch(stopAction)
});
Enter fullscreen mode Exit fullscreen mode

接下来,让我们像这样将它们添加到 connect 函数中:

export default connect(mapStateToProps, mapDispatchToProps)(App);
Enter fullscreen mode Exit fullscreen mode

现在,在我们的 App 组件中,我们可以通过 props 访问 store 状态、startAction 和 stopAction。

让我们把img标签改成:

<img 
  src={logo} 
  className={
    "App-logo" + 
    (this.props.rotating ? "":" App-logo-paused")
  } 
  alt="logo" 
  onClick={
    this.props.rotating ? 
      this.props.stopAction : this.props.startAction
  }
/>
Enter fullscreen mode Exit fullscreen mode

所以,我们在这里表达的是,如果旋转状态(this.props.rotating)为真,那么我们希望将App-logo 类名添加到img 元素中。如果该状态为假,那么我们还希望将App-logo-paused 类名添加到img 元素中。这样就可以暂停动画。

另外,如果this.props.rotatingtrue,那么我们希望将其发送到我们的 store 中,以便在onClick函数中将其改回false,反之亦然。

我们快完成了,但是我们忘记了一件事。

我们还没有告诉我们的 React 应用我们有一个全局状态,或者说,我们使用 Redux 状态管理。

为此,我们进入src/index.js 文件,从react-redux导入Provider,并像这样导入新创建的 store:

import { Provider } from "react-redux";

import configureStore from "store";
Enter fullscreen mode Exit fullscreen mode
  • 提供程序:使 Redux store 可供任何被 connect 函数包裹的嵌套组件使用。

之后,我们不再直接渲染 App 组件,而是通过 Provider 使用我们创建的 store 来渲染它,如下所示:

ReactDOM.render(
  <Provider store={configureStore()}>
    <App />
  </Provider>,
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

在这里,我们可以使用configureStore函数以及其他一些状态,例如configureStore({ rotary: false })

所以,你的index.js 文件应该看起来像这样:

import React from 'react';
import ReactDOM from 'react-dom';
// new imports start
import { Provider } from "react-redux";

import configureStore from "store";
// new imports stop

import './index.css';

import App from './App';
import * as serviceWorker from './serviceWorker';

// changed the render
ReactDOM.render(
  <Provider store={configureStore()}>
    <App />
  </Provider>,
  document.getElementById('root')
);
// changed the render

serviceWorker.unregister();
Enter fullscreen mode Exit fullscreen mode

接下来我们来看看我们的 Redux 应用是否能正常工作:

React 和 Redux 的实际应用
React 和 Redux 实战 |

使用动作创建器

或者,我们可以用动作创建器代替动作,动作创建器是用来创建动作的函数。

这样,我们就可以将两个操作合并到一个函数中,并减少一些代码。

那么,我们来创建一个新文件:

1 — Linux / Mac 命令

touch src/actions/rotateAction.js
Enter fullscreen mode Exit fullscreen mode

2 — Windows 命令

echo "" > src\actions\rotateAction.js
Enter fullscreen mode Exit fullscreen mode

然后添加以下代码:

const rotateAction = (payload) => {
  return {
    type: "rotate",
    payload
  }
}
export default rotateAction;
Enter fullscreen mode Exit fullscreen mode

我们将发送一个类型为 rotate 的操作,有效负载将在 App 组件中获取。

在 src/App.js 组件中,我们需要导入新的 action creator:

import rotateAction from "actions/rotateAction";
Enter fullscreen mode Exit fullscreen mode

将新函数添加到 mapDispatchToProps 中,如下所示:

rotateAction:将接收一个有效载荷,并将有效载荷传递给 rotateAction。

将onClick函数更改为:

onClick={() => this.props.rotateAction(!this.props.rotating)}
Enter fullscreen mode Exit fullscreen mode

最后,将我们新的 action creator 添加到mapDispatchToProps中,如下所示:

rotateAction: (payload) => dispatch(rotateAction(payload))
Enter fullscreen mode Exit fullscreen mode

我们还可以删除旧操作的旧导入,并将其从mapDispatchToProps中删除。

你的 src/App.js 文件应该如下所示:

import React, { Component } from 'react';
// new lines from here
import { connect } from "react-redux";
import rotateAction from "actions/rotateAction";

//// new lines to here

import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    console.log(this.props);
    return (
      <div className="App">
        <header className="App-header">
          <img
            src={logo}
            className={
              "App-logo" +
              (this.props.rotating ? "":" App-logo-paused")
            }
            alt="logo"
            onClick={
              () => this.props.rotateAction(!this.props.rotating)
            }
          />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
    );
  }
}

const mapStateToProps = state => ({
  ...state
});
const mapDispatchToProps = dispatch => ({
  rotateAction: (payload) => dispatch(rotateAction(payload))
});

export default connect(mapStateToProps, mapDispatchToProps)(App);
Enter fullscreen mode Exit fullscreen mode

Paper Dashboard React的实际应用示例

Paper Dashboard React — 产品 Gif
| Paper Dashboard React — 产品 Gif]|

如上图所示,我使用右侧菜单来更改左侧菜单的颜色。这是通过使用组件状态实现的,并将父组件的状态传递给这两个菜单以及一些用于更改该状态的函数。

简要示意图,展示了该应用程序目前的工作原理
|应用当前工作原理简图|

我认为这是一个不错的例子,可以用 Redux 替换这个产品的组件状态。

您可以通过以下三种方式获得它:

git clone https://github.com/creativetimofficial/paper-dashboard-react.git
Enter fullscreen mode Exit fullscreen mode

现在我们有了这个产品,让我们进入它的目录,然后重新安装 redux 和 react-redux:

npm install --save redux react-redux
Enter fullscreen mode Exit fullscreen mode

接下来,我们需要创建操作。由于右侧菜单中有 2 种颜色用于设置左侧菜单的背景色,5 种颜色用于更改链接颜色,因此我们需要 7 个操作,或者 2 个操作创建器——我们选择第二种方案,因为它需要编写的代码更少:

1 — Linux / Mac 命令

mkdir src/actions
touch src/actions/setBgAction.js
touch src/actions/setColorAction.js
Enter fullscreen mode Exit fullscreen mode

2 — Windows 命令

mkdir src\actions
echo "" > src\actions\setBgAction.js
echo "" > src\actions\setColorAction.js
Enter fullscreen mode Exit fullscreen mode

接下来,我们按如下方式创建操作代码:

src/actions/setBgAction.js

const setBgAction = (payload) => {
  return {
    type: "bgChange",
    payload
  }
}
export default setBgAction;
Enter fullscreen mode Exit fullscreen mode

src/actions/setColorAction.js

const setColorAction = (payload) => {
  return {
    type: "colorChange",
    payload
  }
}
export default setColorAction;
Enter fullscreen mode Exit fullscreen mode

现在,和第一部分一样,我们需要 reducer:

1 — Linux / Mac 命令

mkdir src/reducers
touch src/reducers/rootReducer.js
Enter fullscreen mode Exit fullscreen mode

2 — Windows 命令

mkdir src\reducers
echo "" > src\reducers\rootReducer.js
Enter fullscreen mode Exit fullscreen mode

以下是 reducer 的代码:

export default (state, action) => {
  switch (action.type) {
    case "bgChange":
      return {
        ...state,
        bgColor: action.payload
      };
    case "colorChange":
      return {
        ...state,
        activeColor: action.payload
      };
    default:
      return state;
  }
};
Enter fullscreen mode Exit fullscreen mode

如您所见,与第一个例子不同,我们希望保留旧状态并更新其内容。

我们也需要这家店:

1 — Linux / Mac 命令

touch src/store.js
Enter fullscreen mode Exit fullscreen mode

2 — Windows 命令

echo "" > src\store.js
Enter fullscreen mode Exit fullscreen mode

它的代码如下:

import { createStore } from "redux";
import rootReducer from "reducers/rootReducer";

function configureStore(state = { bgColor: "black", activeColor: "info" }) {
  return createStore(rootReducer,state);
}
export default configureStore;
Enter fullscreen mode Exit fullscreen mode

在 src/index.js 文件中,我们需要:

// new imports start
import { Provider } from "react-redux";

import configureStore from "store";
// new imports stop
Enter fullscreen mode Exit fullscreen mode

另外,还要修改渲染函数:

ReactDOM.render(
  <Provider store={configureStore()}>
    <Router history={hist}>
      <Switch>
        {indexRoutes.map((prop, key) => {
          return <Route path={prop.path} key={key} component={prop.component} />;
        })}
      </Switch>
    </Router>
  </Provider>,
  document.getElementById("root")
);
Enter fullscreen mode Exit fullscreen mode

所以index.js文件应该如下所示:

import React from "react";
import ReactDOM from "react-dom";
import { createBrowserHistory } from "history";
import { Router, Route, Switch } from "react-router-dom";
// new imports start
import { Provider } from "react-redux";

import configureStore from "store";
// new imports stop

import "bootstrap/dist/css/bootstrap.css";
import "assets/scss/paper-dashboard.scss";
import "assets/demo/demo.css";

import indexRoutes from "routes/index.jsx";

const hist = createBrowserHistory();

ReactDOM.render(
  <Provider store={configureStore()}>
    <Router history={hist}>
      <Switch>
        {indexRoutes.map((prop, key) => {
          return <Route path={prop.path} key={key} component={prop.component} />;
        })}
      </Switch>
    </Router>
  </Provider>,
  document.getElementById("root")
);
Enter fullscreen mode Exit fullscreen mode

现在我们需要对src/layouts/Dashboard/Dashboard.jsx 文件进行一些修改。我们需要删除状态以及修改状态的函数。请删除以下代码片段

构造函数(第 16 行和第 22 行之间):

constructor(props){
  super(props);
  this.state = {
    backgroundColor: "black",
    activeColor: "info",
  }
}
Enter fullscreen mode Exit fullscreen mode

状态函数(第 41 行至第 46 行之间):

handleActiveClick = (color) => {
    this.setState({ activeColor: color });
  }
handleBgClick = (color) => {
  this.setState({ backgroundColor: color });
}
Enter fullscreen mode Exit fullscreen mode

侧边栏的bgColoractiveColor属性(第 53 行和第 54 行):

bgColor={this.state.backgroundColor}
activeColor={this.state.activeColor}
Enter fullscreen mode Exit fullscreen mode

所有 FixedPlugin 属性(第 59-62 行之间):

bgColor={this.state.backgroundColor}
activeColor={this.state.activeColor}
handleActiveClick={this.handleActiveClick}
handleBgClick={this.handleBgClick}
Enter fullscreen mode Exit fullscreen mode

所以,我们仍然在仪表盘布局组件中使用这段代码:

import React from "react";
// javascript plugin used to create scrollbars on windows
import PerfectScrollbar from "perfect-scrollbar";
import { Route, Switch, Redirect } from "react-router-dom";

import Header from "components/Header/Header.jsx";
import Footer from "components/Footer/Footer.jsx";
import Sidebar from "components/Sidebar/Sidebar.jsx";
import FixedPlugin from "components/FixedPlugin/FixedPlugin.jsx";

import dashboardRoutes from "routes/dashboard.jsx";

var ps;

class Dashboard extends React.Component {
  componentDidMount() {
    if (navigator.platform.indexOf("Win") > -1) {
      ps = new PerfectScrollbar(this.refs.mainPanel);
      document.body.classList.toggle("perfect-scrollbar-on");
    }
  }
  componentWillUnmount() {
    if (navigator.platform.indexOf("Win") > -1) {
      ps.destroy();
      document.body.classList.toggle("perfect-scrollbar-on");
    }
  }
  componentDidUpdate(e) {
    if (e.history.action === "PUSH") {
      this.refs.mainPanel.scrollTop = 0;
      document.scrollingElement.scrollTop = 0;
    }
  }
  render() {
    return (
      <div className="wrapper">
        <Sidebar
          {...this.props}
          routes={dashboardRoutes}
        />
        <div className="main-panel" ref="mainPanel">
          <Header {...this.props} />
          <Switch>
            {dashboardRoutes.map((prop, key) => {
              if (prop.pro) {
                return null;
              }
              if (prop.redirect) {
                return <Redirect from={prop.path} to={prop.pathTo} key={key} />;
              }
              return (
                <Route path={prop.path} component={prop.component} key={key} />
              );
            })}
          </Switch>
          <Footer fluid />
        </div>
        <FixedPlugin />
      </div>
    );
  }
}

export default Dashboard;
Enter fullscreen mode Exit fullscreen mode

我们需要将侧边栏固定插件组件连接到商店。

对于src/components/Sidebar/Sidebar.jsx

import { connect } from "react-redux";
Enter fullscreen mode Exit fullscreen mode

并将导出格式更改为:

const mapStateToProps = state => ({
  ...state
});

export default connect(mapStateToProps)(Sidebar);
Enter fullscreen mode Exit fullscreen mode

对于src/components/FixedPlugin/FixedPlugin.jsx:

js
import { connect } from "react-redux";
import setBgAction from "actions/setBgAction";
import setColorAction from "actions/setColorAction";
Enter fullscreen mode Exit fullscreen mode

出口内容现在应该是:

const mapStateToProps = state => ({
  ...state
});

const mapDispatchToProps = dispatch => ({
  setBgAction: (payload) => dispatch(setBgAction(payload)),
  setColorAction: (payload) => dispatch(setColorAction(payload))
});

export default connect(mapStateToProps, mapDispatchToProps)(FixedPlugin);
Enter fullscreen mode Exit fullscreen mode

我们将进行以下这些更改:

  • 凡是出现handleBgClick这个词的地方,都需要将其更改为setBgAction。
  • 凡是出现handleActiveClick这个词的地方,都需要将其更改为setColorAction。

所以,FixedPlugin 组件现在应该看起来像这样:

import React, { Component } from "react";

import { connect } from "react-redux";
import setBgAction from "actions/setBgAction";
import setColorAction from "actions/setColorAction";

import Button from "components/CustomButton/CustomButton.jsx";

class FixedPlugin extends Component {
  constructor(props) {
    super(props);
    this.state = {
      classes: "dropdown show"
    };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    if (this.state.classes === "dropdown") {
      this.setState({ classes: "dropdown show" });
    } else {
      this.setState({ classes: "dropdown" });
    }
  }
  render() {
    return (
      <div className="fixed-plugin">
        <div className={this.state.classes}>
          <div onClick={this.handleClick}>
            <i className="fa fa-cog fa-2x" />
          </div>
          <ul className="dropdown-menu show">
            <li className="header-title">SIDEBAR BACKGROUND</li>
            <li className="adjustments-line">
              <div className="badge-colors text-center">
                <span
                  className={
                    this.props.bgColor === "black"
                      ? "badge filter badge-dark active"
                      : "badge filter badge-dark"
                  }
                  data-color="black"
                  onClick={() => {
                    this.props.setBgAction("black");
                  }}
                />
                <span
                  className={
                    this.props.bgColor === "white"
                      ? "badge filter badge-light active"
                      : "badge filter badge-light"
                  }
                  data-color="white"
                  onClick={() => {
                    this.props.setBgAction("white");
                  }}
                />
              </div>
            </li>
            <li className="header-title">SIDEBAR ACTIVE COLOR</li>
            <li className="adjustments-line">
              <div className="badge-colors text-center">
                <span
                  className={
                    this.props.activeColor === "primary"
                      ? "badge filter badge-primary active"
                      : "badge filter badge-primary"
                  }
                  data-color="primary"
                  onClick={() => {
                    this.props.setColorAction("primary");
                  }}
                />
                <span
                  className={
                    this.props.activeColor === "info"
                      ? "badge filter badge-info active"
                      : "badge filter badge-info"
                  }
                  data-color="info"
                  onClick={() => {
                    this.props.setColorAction("info");
                  }}
                />
                <span
                  className={
                    this.props.activeColor === "success"
                      ? "badge filter badge-success active"
                      : "badge filter badge-success"
                  }
                  data-color="success"
                  onClick={() => {
                    this.props.setColorAction("success");
                  }}
                />
                <span
                  className={
                    this.props.activeColor === "warning"
                      ? "badge filter badge-warning active"
                      : "badge filter badge-warning"
                  }
                  data-color="warning"
                  onClick={() => {
                    this.props.setColorAction("warning");
                  }}
                />
                <span
                  className={
                    this.props.activeColor === "danger"
                      ? "badge filter badge-danger active"
                      : "badge filter badge-danger"
                  }
                  data-color="danger"
                  onClick={() => {
                    this.props.setColorAction("danger");
                  }}
                />
              </div>
            </li>
            <li className="button-container">
              <Button
                href="https://www.creative-tim.com/product/paper-dashboard-react"
                color="primary"
                block
                round
              >
                Download now
              </Button>
            </li>
            <li className="button-container">
              <Button
                href="https://www.creative-tim.com/product/paper-dashboard-react/#/documentation/tutorial"
                color="default"
                block
                round
                outline
              >
                <i className="nc-icon nc-paper"></i> Documentation
              </Button>
            </li>
            <li className="header-title">Want more components?</li>
            <li className="button-container">
              <Button
                href="https://www.creative-tim.com/product/paper-dashboard-pro-react"
                color="danger"
                block
                round
                disabled
              >
                Get pro version
              </Button>
            </li>
          </ul>
        </div>
      </div>
    );
  }
}

const mapStateToProps = state => ({
  ...state
});

const mapDispatchToProps = dispatch => ({
  setBgAction: (payload) => dispatch(setBgAction(payload)),
  setColorAction: (payload) => dispatch(setColorAction(payload))
});

export default connect(mapStateToProps, mapDispatchToProps)(FixedPlugin);
Enter fullscreen mode Exit fullscreen mode

好了,我们已经完成了,您可以启动项目并查看一切是否运行正常:

创意时间

多个减速器

既然可以有多个 action,也可以有多个 reducer。唯一需要注意的是,你需要将它们组合起来——我们稍后会看到这一点。

接下来,我们为应用程序创建两个新的 reducer,一个用于setBgAction,另一个用于setColorAction

1 — Linux / Mac 命令

touch src/reducers/bgReducer.js
touch src/reducers/colorReducer.js
Enter fullscreen mode Exit fullscreen mode

2 — Windows 命令

echo "" > src\reducers\bgReducer.js
echo "" > src\reducers\colorReducer.js
Enter fullscreen mode Exit fullscreen mode

接下来,我们按如下方式创建 reducer 的代码:

src/reducers/bgReducer.js

export default (state = {}, action) => {
  switch (action.type) {
    case "bgChange":
      return {
        ...state,
        bgColor: action.payload
      };
    default:
      return state;
  }
};
Enter fullscreen mode Exit fullscreen mode

src/reducers/colorReducer.js

export default (state = {} , action) => {
  switch (action.type) {
    case "colorChange":
      return {
        ...state,
        activeColor: action.payload
      };
    default:
      return state;
  }
};
Enter fullscreen mode Exit fullscreen mode

使用组合 reducer 时,需要在每个要组合的 reducer 中添加一个默认状态。在我的例子中,我选择了一个空对象,即state = {};

现在,我们的根Reducer会将这两者结合起来,如下所示:

src/reducers/rootReducer.js

import { combineReducers } from 'redux';

import bgReducer from 'reducers/bgReducer';
import colorReducer from 'reducers/colorReducer';

export default combineReducers({
  activeState: colorReducer,
  bgState: bgReducer
});
Enter fullscreen mode Exit fullscreen mode

因此,我们说我们希望 colorReducer应用程序状态的 activeState 属性引用,bgReducer应用程序状态的bgState属性引用。

这意味着我们州的面貌将不再是现在这样:

state = {
  activeColor: "color1",
  bgColor: "color2"
}
Enter fullscreen mode Exit fullscreen mode

现在它看起来会是这样:

state = {
  activeState: {
    activeColor: "color1"
  },
  bgState: {
    bgColor: "color2"
  }
}
Enter fullscreen mode Exit fullscreen mode

由于我们已经更改了 reducer,现在将它们合并成了一个,因此我们也需要更改store.js 文件:

src/store.js

import { createStore } from "redux";
import rootReducer from "reducers/rootReducer";

// we need to pass the initial state with the new look
function configureStore(state = { bgState: {bgColor: "black"}, activeState: {activeColor: "info"} }) {
  return createStore(rootReducer,state);
}
export default configureStore;
Enter fullscreen mode Exit fullscreen mode

由于我们改变了状态的显示方式,现在需要将SidebarFixedPlugin组件内部的 props 更改为新的状态对象:

src/components/Sidebar/Sidebar.jsx:

第 36 行更改为

<div className="sidebar" data-color={this.props.bgColor} data-active-color={this.props.activeColor}>
Enter fullscreen mode Exit fullscreen mode


<div className="sidebar" data-color={this.props.bgState.bgColor} data-active-color={this.props.activeState.activeColor}>
Enter fullscreen mode Exit fullscreen mode

src/components/FixedPlugin/FixedPlugin.jsx:

我们需要把所有的都改成this.props.bgColorthis.props.bgState.bgColor把所有的都this.props.activeColor改成this.props.activeState.activeColor

所以新代码应该如下所示:

import React, { Component } from "react";

import Button from "components/CustomButton/CustomButton.jsx";

import { connect } from "react-redux";
import setBgAction from "actions/setBgAction";
import setColorAction from "actions/setColorAction";

class FixedPlugin extends Component {
  constructor(props) {
    super(props);
    this.state = {
      classes: "dropdown show"
    };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    if (this.state.classes === "dropdown") {
      this.setState({ classes: "dropdown show" });
    } else {
      this.setState({ classes: "dropdown" });
    }
  }
  render() {
    return (
      <div className="fixed-plugin">
        <div className={this.state.classes}>
          <div onClick={this.handleClick}>
            <i className="fa fa-cog fa-2x" />
          </div>
          <ul className="dropdown-menu show">
            <li className="header-title">SIDEBAR BACKGROUND</li>
            <li className="adjustments-line">
              <div className="badge-colors text-center">
                <span
                  className={
                    this.props.bgState.bgColor === "black"
                      ? "badge filter badge-dark active"
                      : "badge filter badge-dark"
                  }
                  data-color="black"
                  onClick={() => {
                    this.props.setBgAction("black");
                  }}
                />
                <span
                  className={
                    this.props.bgState.bgColor === "white"
                      ? "badge filter badge-light active"
                      : "badge filter badge-light"
                  }
                  data-color="white"
                  onClick={() => {
                    this.props.setBgAction("white");
                  }}
                />
              </div>
            </li>
            <li className="header-title">SIDEBAR ACTIVE COLOR</li>
            <li className="adjustments-line">
              <div className="badge-colors text-center">
                <span
                  className={
                    this.props.activeState.activeColor === "primary"
                      ? "badge filter badge-primary active"
                      : "badge filter badge-primary"
                  }
                  data-color="primary"
                  onClick={() => {
                    this.props.setColorAction("primary");
                  }}
                />
                <span
                  className={
                    this.props.activeState.activeColor === "info"
                      ? "badge filter badge-info active"
                      : "badge filter badge-info"
                  }
                  data-color="info"
                  onClick={() => {
                    this.props.setColorAction("info");
                  }}
                />
                <span
                  className={
                    this.props.activeState.activeColor === "success"
                      ? "badge filter badge-success active"
                      : "badge filter badge-success"
                  }
                  data-color="success"
                  onClick={() => {
                    this.props.setColorAction("success");
                  }}
                />
                <span
                  className={
                    this.props.activeState.activeColor === "warning"
                      ? "badge filter badge-warning active"
                      : "badge filter badge-warning"
                  }
                  data-color="warning"
                  onClick={() => {
                    this.props.setColorAction("warning");
                  }}
                />
                <span
                  className={
                    this.props.activeState.activeColor === "danger"
                      ? "badge filter badge-danger active"
                      : "badge filter badge-danger"
                  }
                  data-color="danger"
                  onClick={() => {
                    this.props.setColorAction("danger");
                  }}
                />
              </div>
            </li>
            <li className="button-container">
              <Button
                href="https://www.creative-tim.com/product/paper-dashboard-react"
                color="primary"
                block
                round
              >
                Download now
              </Button>
            </li>
            <li className="button-container">
              <Button
                href="https://www.creative-tim.com/product/paper-dashboard-react/#/documentation/tutorial"
                color="default"
                block
                round
                outline
              >
                <i className="nc-icon nc-paper"></i> Documentation
              </Button>
            </li>
            <li className="header-title">Want more components?</li>
            <li className="button-container">
              <Button
                href="https://www.creative-tim.com/product/paper-dashboard-pro-react"
                color="danger"
                block
                round
                disabled
              >
                Get pro version
              </Button>
            </li>
          </ul>
        </div>
      </div>
    );
  }
}

const mapStateToProps = state => ({
  ...state
});

const mapDispatchToProps = dispatch => ({
  setBgAction: (payload) => dispatch(setBgAction(payload)),
  setColorAction: (payload) => dispatch(setColorAction(payload))
});

export default connect(mapStateToProps, mapDispatchToProps)(FixedPlugin);
Enter fullscreen mode Exit fullscreen mode

让我们再次打开项目npm start,看看一切是否正常。锵锵!

感谢阅读!

如果您喜欢这篇教程,请分享。我很想听听您的想法。只需在本帖下方留言,我非常乐意回复。

还要特别感谢Esther Falayi 的教程,它让我对Redux有了急需的理解

实用链接:

请在以下平台找到我:

文章来源:https://dev.to/creativetim_official/how-to-use-redux-in-reactjs-with-real-life-examples-4gog