redux 使用示例_如何通过实际示例在ReactJS中使用Redux

redux 使用示例

by Nazare Emanuel Ioan

由Nazare Emanuel Ioan

如何通过实际示例在ReactJS中使用Redux (How to use Redux in ReactJS with real-life examples)

Since I started to work with ReactJS, at Creative-Tim, I’ve only used it to create simple react apps, or templates if you will. I have used ReactJS only with create-react-app and have never tried to integrate it with something more.

自从我开始在Creative-Tim使用ReactJS以来,我仅使用它来创建简单的react应用程序模板(如果需要)。 我只将ReactJS与create-react-app一起使用 ,并且从未尝试将其与其他功能集成。

A lot of our users have asked me or my team if the templates created by me had Redux on them. Or if they were created in such manner that they could be used with Redux. And my answer was always something like: “I haven’t worked with Redux yet and I do not know what answer I should give you”.

我们的许多用户都问我或我的团队,我创建的模板上是否包含Redux 。 或者,如果它们是以可以与Redux一起使用的方式创建的。 我的回答总是这样:“我还没有与Redux合作,我不知道应该给你什么答案”。

So here I am now, writing an article about Redux and how it should be used in React. Later on, in this article, I am going to add Redux on top of one of the projects that I have worked over the last one and some years.

所以现在我在这里写一篇关于Redux的文章,以及如何在React中使用它。 稍后,在本文中,我将把Redux添加到我在过去和几年中工作过的一个项目之上。

Good to know before we go ahead and wrestle with these two libraries:

在继续研究这两个库之前,要知道一点:

  • I am going to use create-react-app@2.1.1 (installed globally)

    我将使用create-react-app@2.1。 1个(全局安装)

  • I am using npm@6.4.1

    我正在使用npm@6.4.1

  • My Node.js version at the time of writing this post was 10.13.0 (LTS)

    在撰写本文时,我的Node.js版本是10.13.0(LTS)

  • If you want to use Webpack instead, then you can read my Webpack article, and combine what I am showing you there with what I am going to show you here.

    如果您想改用Webpack ,那么您可以阅读我的Webpack文章 ,并将我在那里展示的内容与在这里向您展示的内容结合起来。

创建一个新的基于ReactJS的项目并向其中添加Redux (Creating a new ReactJS based project and adding Redux to it)

First things first let’s create a new react app, cd into it and start it.

首先,让我们创建一个新的react应用,将其CD并启动。

create-react-app react-redux-tutorial
cd react-redux-tutorial
npm start

As we can see, create-react-app gives us a very basic template with a paragraph, an anchor to the React website and the official ReactJS icon rotating.

如我们所见,create-react-app为我们提供了一个非常基本的模板,其中包含一个段落,React网站的锚点和官方ReactJS图标的旋转。

I haven’t told you guys what we are going to use Redux for, or what are we doing here. And this is because I needed the above gif image.

我还没有告诉大家我们将使用Redux做什么,或者我们在这里做什么。 这是因为我需要上面的gif图像。

To make this tutorial article light weight and easy to understand, we are not going to build something very complex. We are going to use Redux to make the above React image stop or start rotating.

为了使本教程文章轻巧易懂,我们将不构建非常复杂的东西。 我们将使用Redux使上面的React图像停止或开始旋转。

So this being said, let’s go ahead and add the following Redux packages:

话虽如此,让我们继续添加以下Redux软件包:

npm install --save redux react-redux

redux v4.0.1

redux v4.0.1

  • What Redux does in a very general sense, is that it creates a global state for the whole application, that can be accessed by any of your component

    Redux在很一般的意义上所做的是,它为整个应用程序创建了一个全局状态,可以由您的任何组件访问
  • It is a state management library

    这是一个状态管理库
  • You have only one state for your whole app, and not states for each of your components

    整个应用程序只有一个状态,而每个组件都没有状态

react-redux v5.1.1

react-redux v5.1.1

  • This is used so we can access Redux’s data and modify it by sending actions to Redux — actually not Redux, but we’ll get there

    使用它是为了我们可以访问Redux的数据并通过将操作发送到Redux来修改它-实际上不是Redux,但是我们会到达那里
  • The official docs state: It lets your React components read data from a Redux store, and dispatch actions to the store to update data

    官方文档说明: 它使您的React组件可以从Redux存储中读取数据,并向存储分派操作以更新数据

NOTE: If you have problems with the above command, try installing the packages separately

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

When working with Redux, you will need three main things:

使用Redux时,您需要做三件事:

  • actions: these are objects that should have two properties, one describing the type of action, and one describing what should be changed in the app state.

    actions :这些对象应具有两个属性,一个属性描述操作的类型 ,另一个属性描述应在应用程序状态下进行的更改。

  • reducers: these are functions that implement the behavior of the actions. They change the state of the app, based on the action description and the state change description.

    减速器 :这些是实现动作行为的功能。 他们根据操作说明和状态更改说明更改应用程序的状态。

  • store: it brings the actions and reducers together, holding and changing the state for the whole app — there is only one store.

    store :将行动和简化程序结合在一起,保持和更改整个应用程序的状态-只有一个商店。

As I’ve said above, we are going to stop and start the React logo spinning. This means we are going to need two actions as follows:

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

1 — Linux / Mac commands

1-Linux / Mac命令

mkdir src/actions
touch src/actions/startAction.js
touch src/actions/stopAction.js

2 — Windows commands

2-Windows命令

mkdir src\actions
echo "" > src\actions\startAction.js
echo "" > src\actions\stopAction.js

Now let’s edit the src/actions/startAction.js as follows:

现在,如下所示编辑src / actions / startAction.js

export const startAction = {
  type: "rotate",
  payload: true
};

So, we are going to say to our reducer that the type of the action is about the rotation (rotate) of the React logo. And the state for the rotate of the React logo should be changed to true — we want the logo to start rotating.

因此,我们要对化简说,动作的类型是关于React徽标的旋转 ( rotate )的。 而且,React徽标的旋转状态应更改为true-我们希望徽标开始旋转。

Now let’s edit the src/actions/stopAction.js as follows:

现在,如下所示编辑src / actions / stopAction.js

export const stopAction = {
  type: "rotate",
  payload: false
};

So, we are going to say to our reducer that the type of the action is about the rotation (rotate) of the React logo. And the state for the rotate of the React logo should be changed to false — we want the logo to stop rotating.

因此,我们要对化简说,动作的类型是关于React徽标的旋转 ( rotate )的。 而且,React徽标的旋转状态应更改为false-我们希望徽标停止旋转。

Let’s also create the reducer for our app:

让我们还为我们的应用程序创建reducer:

1 — Linux / Mac commands

1-Linux / Mac命令

mkdir src/reducers
touch src/reducers/rotateReducer.js

2 — Windows commands

2-Windows命令

mkdir src\reducers
echo "" > src\reducers\rotateReducer.js

And, add the following code inside of it:

并且,在其中添加以下代码:

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

So, the reducer will receive both of our actions, both of which are of type rotate, and they both change the same state in the app — which is state.rotating. Based on the payload of these actions, state.rotating will change into true or false.

因此,reducer将接收我们的两个动作,两个动作都是rotate类型并且它们都更改了应用程序中的相同状态,即state.rotating 。 根据这些操作的有效负载, state.rotating将更改为truefalse

I’ve added a default case, which will keep the state unaltered if the action type is not rotate. The default value is there in case we create an action and we forget to add a case for that action. This way we do not delete the whole app state — we simply do nothing, and keep what we had.

我添加了一个默认的情况下,将保持不变状态,如果动作类型是不能转动 。 如果我们创建一个动作,而忘记为该动作添加案例,则存在默认值。 这样,我们就不会删除整个应用程序状态-我们只是不执行任何操作,并保留已拥有的内容。

The last thing that we need to do is to create our store for the whole app. Since there is only one store / one state for the whole app, we are not going to create a new folder for the store. If you want, you can create a new folder for the store and add it there, but it’s not like with the actions, for example, where you can have multiple actions and it looks better to keep them inside a folder.

我们需要做的最后一件事是为整个应用程序创建商店。 由于整个应用程序只有一个商店/一个状态,因此我们不会为商店创建一个新文件夹。 如果需要,您可以为商店创建一个新文件夹并将其添加到该文件夹​​中,但是与操作不同,例如,可以在其中进行多个操作,最好将它们保存在一个文件夹中。

So this being said we are going to run this command:

因此,我们要运行以下命令:

1 — Linux / Mac command

1-Linux / Mac命令

touch src/store.js

2 — Windows command

2-Windows命令

echo "" > src\store.js

And also add the following code inside it:

并在其中添加以下代码:

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

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

export default configureStore;

So, we create a function named configureStore in which we send a default state, and we create our store using the created reducer and the default state.

因此,我们创建了一个名为configureStore的函数,在其中发送默认状态,并使用创建的reducer和默认状态创建商店。

I’m not sure if you’ve seen my imports, they use absolute paths, so you might have some errors due to this. The fix for this is one of the two:

我不确定您是否看到过我的导入文件,它们使用绝对路径,因此您可能会有一些错误。 解决方法是以下两种方法之一:

Either

要么

1 — Add a .env file into your app like so:

1-将.env文件添加到您的应用中,如下所示:

echo "NODE_PATH=./src" > .env

Or

要么

2 — Install cross-env globally and change the start script from the package.json file like so:

2 —全局安装cross-env并从package.json文件更改启动脚本,如下所示:

npm install -g cross-env

And inside package.json

在package.json里面

"start": "NODE_PATH=./src react-scripts start",

Now that we have set up our store, our actions and our reducer we need to add a new class inside the src/App.css file. This class will pause the rotating animation of the logo.

现在我们已经建立了商店,动作和Reducer,我们需要在src / App.css文件中添加一个新类。 此类将暂停徽标的旋转动画。

So we are going to write the following inside src/App.css:

因此,我们将在src / App.css中编写以下内容

.App-logo-paused {
  animation-play-state: paused;
}

So your App.css file should look something like this:

因此,您的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);
  }
}

Now, we only need to modify our src/App.js file so that it listens to our store state. And when clicking on the logo, it calls one of the start or stop actions.

现在,我们只需要修改src / App.js文件,使其监听我们的商店状态即可。 当单击徽标时,它将调用开始或停止操作之一。

First things first, we need to connect our component to our redux store so we import connect from react-redux.

首先,我们需要将组件连接到我们的redux存储,以便从react-redux导入connect

import { connect } from "react-redux";

After this, we’ll export our App component through the connect method like this:

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

export default connect()(App);

To change the redux store state we’ll need the actions that we’ve done earlier so let’s import them as well:

要更改redux存储状态,我们需要前面已经完成的操作,因此我们也将其导入:

import { startAction } from "actions/startAction";
import { stopAction } from "actions/stopAction";

Now we need to retrieve the state from our store and to say that we want the start and stop actions to be used for changing the state.

现在,我们需要从商店中检索状态,并说我们希望将start和stop操作用于更改状态。

This will be done using the connect function, which accepts two parameters:

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

  • mapStateToProps: this is used to retrieve the store state

    mapStateToProps:用于检索商店状态

  • mapDispatchToProps: this is used to retrieve the actions and dispatch them to the store

    mapDispatchToProps:用于检索动作并将其分发到商店

You can read more about them here: react-redux connect function arguments.

您可以在这里阅读更多有关它们的信息: react-redux connect function arguments

So let’s write inside our App.js (at the end of the file if you may):

因此,让我们在App.js中编写(如果可能,在文件末尾):

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

const mapDispatchToProps = dispatch => ({
  startAction: () => dispatch(startAction),
  stopAction: () => dispatch(stopAction)
});

After this, let’s add them inside our connect function like so:

之后,让我们将它们添加到我们的connect函数中,如下所示:

export default connect(mapStateToProps, mapDispatchToProps)(App);

And right now, inside our App component, we can access the store state, the startAction and stopAction through props.

现在,在我们的App组件内部,我们可以通过道具访问商店状态,startAction和stopAction。

Let’s change the img tag to:

让我们将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
  }
/>

So, what we are saying here is, if the store state of rotating (this.props.rotating) is true, then we want just the App-logo className to be set to our img. If that is false, then we also want the App-logo-paused class to be set in the className. This way we pause the animation.

因此,在这里我们要说的是,如果rotation ( this.props.rotating )的存储状态为true,那么我们只希望将App-logo className设置为img 。 如果那是错误的,那么我们也希望在className中设置App-logo-paused类。 这样我们就可以暂停动画。

Also, if this.props.rotating is true, then we want to send to our store for the onClick function and change it back to false, and vice-versa.

同样,如果this.props.rotatingtrue ,那么我们要发送给我们的商店以使用onClick函数,并将其更改回false ,反之亦然。

We are almost done, but we’ve forgot something.

我们差不多完成了,但是我们忘记了一些事情。

We haven’t yet told our react app that we have a global state, or if you will, that we use redux state management.

我们还没有告诉React应用程序我们具有全局状态,或者如果愿意,我们会使用Redux状态管理。

For this, we go inside src/index.js, we import a Provider from react-redux, and the newly created store like so:

为此,我们进入src / index.js ,从react-redux和新创建的商店中导入Provider ,如下所示:

import { Provider } from "react-redux";

import configureStore from "store";
  • Provider: makes the Redux store available to any nested components that have been wrapped in the connect function

    Provider :使Redux存储可用于已被connect函数包装的任何嵌套组件

After this, instead of rendering our App component directly, we render it through our Provider using the store that we’ve created like so:

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

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

Here we could have used the configureStore function with some other state, for example configureStore({ rotating: false }).

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

So, your index.js should look like this:

因此,您的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();

Let’s go ahead and see if our redux app works:

让我们继续来看一下我们的redux应用程序是否有效:

使用动作创作者 (Using action creators)

Optionally, instead of actions, we can use action creators, which are functions that create actions.

(可选)代替动作 ,我们可以使用动作创建者 ,它们是创建动作的函数。

This way, we can combine our two actions in just one function and reduce a bit our code.

这样,我们可以将两个动作组合在一个函数中,并减少代码量。

So, let’s go ahead and create a new file:

因此,让我们继续创建一个新文件:

1 — Linux / Mac command

1-Linux / Mac命令

touch src/actions/rotateAction.js

2 — Windows command

2-Windows命令

echo "" > src\actions\rotateAction.js

And add this code:

并添加以下代码:

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

We are going to send an action of type rotate, with a payload that we are going to get in the App component.

我们将发送类型为Rotate的动作,并带有要在App组件中获得的有效负载。

Inside the src/App.js component, we need to import our new action creator:

在src / App.js组件内部,我们需要导入新的动作创建者:

import rotateAction from "actions/rotateAction";

Add the new function to the mapDispatchToProps like so:

像这样将新函数添加到mapDispatchToProps:

rotateAction: will receive a (payload) and will dispatch the rotateAction with the payload

rotateAction:将收到一个(有效载荷)并将分派带有有效载荷的rotateAction

Change the onClick function to:

onClick函数更改为:

onClick={() => this.props.rotateAction(!this.props.rotating)}

And finally, add our new action creator to the mapDispatchToProps like this:

最后,将我们的新动作创建者添加到mapDispatchToProps中,如下所示:

rotateAction: (payload) => dispatch(rotateAction(payload))

We can also delete the old imports for the old actions, and delete them from the mapDispatchToProps as well.

我们还可以删除旧操作的旧导入,也可以从mapDispatchToProps中删除它们。

This is how you new src/App.js should look like:

这是新的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);

Paper Dashboard React的真实示例 (A real-life example with Paper Dashboard React)

As you will see in the above gif image, I am using the right menu to change the colors of the menu on the left. This is achieved by using component states, and by passing that state from a parent component to the two menus and some functions to change that state.

正如您将在上面的gif图像中看到的那样,我正在使用右侧菜单更改左侧菜单的颜色。 这是通过使用组件状态并将该状态从父组件传递到两个菜单以及一些用于更改该状态的函数来实现的。

I thought it would be a nice example, to take this product and replace the component states with Redux.

我认为这将是一个很好的示例,可以带上该产品并将组件状态替换为Redux。

You can get it in these 3 ways:

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

  1. Download from creative-tim.com

    creative-tim.com下载

  2. Download from Github

    Github下载

  3. Clone from Github:

    从Github克隆:
git clone https://github.com/creativetimofficial/paper-dashboard-react.git

Now that we have this product, let’s cd into it and install again redux and react-redux:

现在我们有了这个产品,让我们进入它并再次安装redux和react-redux:

npm install --save redux react-redux

After this, we need to create the actions. Since in the right menu we have 2 colors that set the background of the left menu, and 5 colors that change the color of the links, we need 7 actions, or 2 actions creators — and we are going with this second option since it is a bit less code to write:

此后,我们需要创建操作。 由于在右侧菜单中,我们有2种颜色可以设置左侧菜单的背景,而在5种颜色中可以更改链接的颜色,因此我们需要7个动作或2个动作创建者-我们将选择第二个选项,因为它是少写一些代码:

1 — Linux / Mac commands

1-Linux / Mac命令

mkdir src/actions
touch src/actions/setBgAction.js
touch src/actions/setColorAction.js

2 — Windows commands

2-Windows命令

mkdir src\actions
echo "" > src\actions\setBgAction.js
echo "" > src\actions\setColorAction.js

After this, let’s create the actions code as follows:

之后,让我们如下创建动作代码:

src/actions/setBgAction.js

src / actions / setBgAction.js

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

src/actions/setColorAction.js

src / actions / setColorAction.js

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

Now, as in the first part, we need the reducer:

现在,与第一部分一样,我们需要减速器:

1 — Linux / Mac commands

1-Linux / Mac命令

mkdir src/reducers
touch src/reducers/rootReducer.js

2 — Windows commands

2-Windows命令

mkdir src\reducers
echo "" > src\reducers\rootReducer.js

And the code for the reducer:

以及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;
  }
};

As you can see here, unlike our first example, we want to keep our old state and update its contents.

正如您在这里看到的,与我们的第一个示例不同,我们想要保留旧状态并更新其内容。

We also need the store:

我们还需要商店:

1 — Linux / Mac command

1-Linux / Mac命令

touch src/store.js

2 — Windows command

2-Windows命令

echo "" > src\store.js

The code for it:

它的代码:

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

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

Inside the src/index.js we need:

在src / index.js内部,我们需要:

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

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

And also, change the render function:

另外,更改渲染功能:

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")
);

So the index.js file should look like this:

因此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")
);

Now we need to make some changes inside src/layouts/Dashboard/Dashboard.jsx. We need to delete the state and the functions that change the state. So go ahead and delete these bits of code:

现在我们需要在src / layouts / Dashboard / Dashboard.jsx中进行一些更改。 我们需要删除状态和更改状态的函数。 因此,继续删除以下代码:

The constructor (between lines 16 and 22):

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

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

The state functions (between lines 41 and 46):

状态功能(在第41和46行之间):

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

The sidebar bgColor and activeColor props (lines 53 and 54):

侧边栏bgColoractiveColor道具(第53和54行):

bgColor={this.state.backgroundColor}
activeColor={this.state.activeColor}

All of the FixedPlugin props (between lines 59–62):

所有FixedPlugin道具(在59-62行之间):

bgColor={this.state.backgroundColor}
activeColor={this.state.activeColor}
handleActiveClick={this.handleActiveClick}
handleBgClick={this.handleBgClick}

So, we remain with this code inside the Dashboard layout component:

因此,我们将以下代码保留在仪表板布局组件中:

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;

We need to connect the Sidebar and FixedPlugin components to to the store.

我们需要将补充工具栏FixedPlugin组件连接到商店。

For src/components/Sidebar/Sidebar.jsx:

对于src / components / Sidebar / Sidebar.jsx

import { connect } from "react-redux";

And change the export to:

并将导出更改为:

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

export default connect(mapStateToProps)(Sidebar);

For the src/components/FixedPlugin/FixedPlugin.jsx:

对于src / components / FixedPlugin / FixedPlugin.jsx

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

And the export should now be:

现在,导出应为:

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

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

export default connect(mapStateToProps, mapDispatchToProps)(FixedPlugin);

We are going to have these next changes:

接下来我们将进行以下更改:

  • anywhere you find the word handleBgClick, you’ll need to change it to setBgAction

    在找到handleBgClick单词的任何地方,都需要将其更改为setBgAction

  • anywhere you find the word handleActiveClick, you’ll need to change it to setColorAction

    在找到handleActiveClick这个词的任何地方,都需要将其更改为setColorAction

So, the FixedPlugin component should now look like this:

因此,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);

And we are done, you can start the project and see how everything works fine:

至此,您可以启动项目并查看一切正常情况:

多个异径管 (Multiple reducers)

As you can have multiple actions, you can have multiple reducers. The only thing is that you need to combine them — we’ll see this a bit further down.

由于可以有多个动作,因此可以有多个减速器。 唯一的事情是您需要将它们组合在一起-我们将看到一点点进一步。

Let’s go ahead and create two new reducers for our app, one for the setBgAction and one for the setColorAction:

让我们继续为我们的应用程序创建两个新的reducer,一个用于setBgAction ,另一个用于setColorAction

1 — Linux / Mac commands

1-Linux / Mac命令

touch src/reducers/bgReducer.js
touch src/reducers/colorReducer.js

2 — Windows commands

2-Windows命令

echo "" > src\reducers\bgReducer.js
echo "" > src\reducers\colorReducer.js

After this, let’s create the reducers’ code as follows:

之后,让我们按如下方式创建化简器的代码:

src/reducers/bgReducer.js

src / reducers / bgReducer.js

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

src/reducers/colorReducer.js

src / reducers / colorReducer.js

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

When working with combined reducers, you need to add a default state in each of your reducers that are going to be combined. In my case, I’ve chosen an empty object i.e. state = {};

使用组合式减速器时,您需要在将要组合的每个减速器中添加默认状态 。 就我而言,我选择了一个空对象,即state = {} ;

And now, our rootReducer will combine these two as follows:

现在,我们的rootReducer将把这两个结合如下:

src/reducers/rootReducer.js

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
});

So, we say that we want the colorReducer to be referred by the activeState prop of the app state, and the bgReducer to be referred by the bgState prop of the app state.

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

This means that our state will no longer look like this:

这意味着我们的状态将不再像这样:

state = {
  activeColor: "color1",
  bgColor: "color2"
}

It will now look like this:

现在看起来像这样:

state = {
  activeState: {
    activeColor: "color1"
  },
  bgState: {
    bgColor: "color2"
  }
}

Since we’ve changed our reducers, now we’ve now combined them together into just one, we need to change our store.js as well:

由于我们已经更改了减速器,现在我们将它们简化为一个,因此我们还需要更改store.js

src/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;

Since we’ve changed the way the state looks, we now need to change the props inside the Sidebar and FixedPlugin components to the new state object:

由于我们更改了状态的外观,因此我们现在需要将补充工具栏FixedPlugin组件内的道具更改为新的状态对象:

src/components/Sidebar/Sidebar.jsx:

src / components / Sidebar / Sidebar.jsx

Change line 36 from

第36行

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

to

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

src/components/FixedPlugin/FixedPlugin.jsx:

src / components / FixedPlugin / FixedPlugin.jsx

We need to change all the this.props.bgColor to this.props.bgState.bgColor . And all the this.props.activeColor to this.props.activeState.activeColor .

我们需要将所有this.props.bgColor更改为this.props.bgState.bgColor 。 以及所有this.props.activeColorthis.props.activeState.activeColor

So the new code should look like this:

因此,新代码应如下所示:

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);

Let’s open the project again with npm start and see how everything works. Ta da!

让我们再次使用npm start打开项目,看看一切如何进行。 da!

谢谢阅读! (Thanks for reading!)

If you’ve enjoyed reading this tutorial please share it. I am very keen on hearing your thoughts about it. Just give this thread a comment and I’ll be more than happy to reply.

如果您喜欢阅读本教程,请分享。 我非常希望听到您对此的看法。 只需对此线程发表评论,我将非常乐意答复。

Special thanks should also go to Esther Falayi for his tutorial which has given me some much needed understanding on Redux.

特别感谢Esther Falayi教程 ,该教程使我对Redux有了一些急需的理解。

Useful links:

有用的链接:

Find me on:

在以下位置找到我:

翻译自: https://www.freecodecamp.org/news/how-to-use-redux-in-reactjs-with-real-life-examples-687ab4441b85/

redux 使用示例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值