С тех пор как я начал работать с ReactJS в Creative-Tim, я использовал его только для создания простых приложений для реагирования или шаблонов, если хотите. Я использовал ReactJS только с create-react-app и никогда не пытался интегрировать его с чем-то еще.
Многие наши пользователи спрашивали меня или мою команду, есть ли в созданных мной шаблонах Redux. Или если бы они были созданы таким образом, чтобы их можно было использовать с Redux. И мой ответ всегда был примерно таким: Я еще не работал с Redux и не знаю, какой ответ дать вам.
Итак, я сейчас пишу статью о Redux и о том, как его следует использовать в React. Позже, в этой статье, я собираюсь добавить Redux поверх одного из проектов, над которыми я работал последние год с лишним.
Полезно знать, прежде чем мы продолжим и разберемся с этими двумя библиотеками:
- Я собираюсь использовать [email protected]. 1 (установлен глобально)
- Я использую [email protected]
- Моя версия Node.js на момент написания этого поста была 10.13.0 (LTS)
- Если вы хотите использовать вместо этого Webpack, то вы можете прочитать мою статью о Webpack и объединить то, что я вам здесь показываю, с тем, что я собираюсь показать вам здесь.
Создание нового проекта на основе ReactJS и добавление в него Redux
Прежде всего, давайте создадим новое приложение для реагирования, войдем в него и запустим.
create-react-app react-redux-tutorial cd react-redux-tutorial npm start
Как мы видим, приложение create-react-app дает нам очень простой шаблон с абзацем, привязкой к веб-сайту React и официальным вращающимся значком ReactJS.
Я не сказал вам, ребята, для чего мы собираемся использовать Redux или что мы здесь делаем. И это потому, что мне понадобилось приведенное выше изображение в формате gif.
Чтобы сделать эту учебную статью легкой и понятной, мы не собираемся создавать что-то очень сложное. Мы собираемся использовать Redux, чтобы остановить или начать вращение вышеуказанного изображения React.
Итак, давайте продолжим и добавим следующие пакеты Redux:
npm install --save redux react-redux
- Что Redux делает в очень общем смысле, так это то, что он создает глобальное состояние для всего приложения, к которому может получить доступ любой из ваших компонентов.
- Это библиотека государственного управления
- У вас есть только одно состояние для всего приложения, а не состояния для каждого из ваших компонентов.
- Это используется для того, чтобы мы могли получить доступ к данным Redux и изменить их, отправив действия в Redux - на самом деле не Redux, но мы доберемся туда.
- В официальной документации говорится: Он позволяет вашим компонентам React считывать данные из хранилища Redux и отправлять действия в хранилище для обновления данных
ПРИМЕЧАНИЕ: Если у вас возникли проблемы с приведенной выше командой, попробуйте установить пакеты отдельно
При работе с Redux вам понадобятся три основных вещи:
- Действия: это объекты, которые должны иметь два свойства, одно из которых описывает тип действия, а другое - то, что следует изменить в состоянии приложения.
- Редукторы: это функции, реализующие поведение действий. Они изменяют состояние приложения на основе описания действия и описания изменения состояния.
- Store: он объединяет действия и редукторы, удерживая и изменяя состояние для всего приложения - есть только одно хранилище.
Как я уже сказал выше, мы собираемся остановиться и начать вращение логотипа React. Это означает, что нам потребуются два следующих действия:
1 - Команды Linux / Mac
mkdir src/actions touch src/actions/startAction.js touch src/actions/stopAction.js
2 - Команды Windows
mkdir src\actions echo "" > src\actions\startAction.js echo "" > src\actions\stopAction.js
Теперь давайте отредактируем src / actions / startAction.js следующим образом:
export const startAction = { type: "rotate", payload: true };
Итак, мы собираемся сказать нашему редуктору, что тип действия касается поворота (поворота) логотипа React. И состояние поворота логотипа React должно быть изменено на true - мы хотим, чтобы логотип начал вращаться.
Теперь давайте отредактируем src / actions / stopAction.js следующим образом:
export const stopAction = { type: "rotate", payload: false };
Итак, мы собираемся сказать нашему редуктору, что тип действия касается поворота (поворота) логотипа React. И состояние поворота логотипа React должно быть изменено на false - мы хотим, чтобы логотип перестал вращаться.
Давайте также создадим редуктор для нашего приложения:
1 - Команды Linux / Mac
mkdir src/reducers touch src/reducers/rotateReducer.js
2 - Команды Windows
mkdir src\reducers echo "" > src\reducers\rotateReducer.js
И добавьте в него следующий код:
export default (state, action) => { switch (action.type) { case "rotate": return { rotating: action.payload }; default: return state; } };
Таким образом, редуктор получит оба наших действия, оба имеют тип rotate, и оба они изменят одно и то же состояние в приложении - state.rotating. В зависимости от полезной нагрузки этих действий state.rotating изменится на true или false.
Я добавил регистр по умолчанию, который не меняет состояние, если тип действия не повернуть. По умолчанию используется значение на тот случай, если мы создадим действие и забудем добавить кейс для этого действия. Таким образом, мы не удаляем все состояние приложения - мы просто ничего не делаем и сохраняем то, что у нас было.
Последнее, что нам нужно сделать, это создать наш магазин для всего приложения. Поскольку для всего приложения существует только одно хранилище / одно состояние, мы не собираемся создавать новую папку для магазина. При желании вы можете создать новую папку для магазина и добавить ее туда, но это не похоже на действия, например, где у вас может быть несколько действий, и лучше держать их внутри папки.
Итак, как говорится, мы собираемся запустить эту команду:
1 - команда Linux / Mac
touch src/store.js
2 - команда Windows
echo "" > src\store.js
А также добавьте в него следующий код:
import { createStore } from "redux"; import rotateReducer from "reducers/rotateReducer"; function configureStore(state = { rotating: true }) { return createStore(rotateReducer,state); } export default configureStore;
Итак, мы создаем функцию с именем configureStore, в которой мы отправляем состояние по умолчанию, и мы создаем наше хранилище, используя созданный редуктор и состояние по умолчанию.
Я не уверен, видели ли вы мой импорт, они используют абсолютные пути, поэтому у вас могут быть ошибки из-за этого. Исправление для этого - одно из двух:
Или
1 - Добавьте файл .env в свое приложение следующим образом:
echo "NODE_PATH=./src" > .env
Or
2 - Установите cross-env глобально и измените сценарий запуска из файла package.json следующим образом:
npm install -g cross-env
И внутри package.json
"start": "NODE_PATH=./src react-scripts start",
Теперь, когда мы настроили наше хранилище, наши действия и наш редуктор, нам нужно добавить новый класс в файл src / App.css. Этот класс приостановит вращающуюся анимацию логотипа.
Итак, мы собираемся написать следующее внутри src / App.css:
.App-logo-paused { animation-play-state: paused; }
Итак, ваш файл App.css должен выглядеть примерно так (если вы не используете ту же версию create-response-app, просто скопируйте приведенный ниже код):
.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); } }
Теперь нам нужно только изменить наш файл src / App.js, чтобы он прослушивал состояние нашего хранилища. А при нажатии на логотип вызывает одно из действий запуска или остановки.
Прежде всего, нам нужно подключить наш компонент к нашему хранилищу redux, поэтому мы импортируем connect из response-redux.
import { connect } from "react-redux";
После этого мы экспортируем наш компонент приложения с помощью метода подключения следующим образом:
export default connect()(App);
Чтобы изменить состояние хранилища redux, нам потребуются действия, которые мы сделали ранее, поэтому давайте также импортируем их:
import { startAction } from "actions/startAction"; import { stopAction } from "actions/stopAction";
Теперь нам нужно получить состояние из нашего хранилища и сказать, что мы хотим, чтобы действия start и stop использовались для изменения состояния.
Это будет сделано с помощью функции подключения, которая принимает два параметра:
- mapStateToProps: используется для получения состояния хранилища.
- mapDispatchToProps: используется для получения действий и отправки их в магазин.
Подробнее о них можно прочитать здесь: аргументы функции react-redux connect.
Итак, давайте напишем внутри нашего App.js (если можно, в конце файла):
const mapStateToProps = state => ({ ...state }); const mapDispatchToProps = dispatch => ({ startAction: () => dispatch(startAction), stopAction: () => dispatch(stopAction) });
После этого давайте добавим их в нашу функцию подключения следующим образом:
export default connect(mapStateToProps, mapDispatchToProps)(App);
И прямо сейчас внутри нашего компонента App мы можем получить доступ к состоянию хранилища, startAction и stopAction через props.
Давайте изменим тег 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 } />
Итак, мы говорим, что если состояние вращения хранилища (this.props.rotating) истинно, то нам нужен только логотип приложения className для нашего img. Если это неверно, мы также хотим, чтобы класс App-logo-paused был установлен в className. Таким образом мы приостанавливаем анимацию.
Кроме того, если this.props.rotating имеет значение true, мы хотим отправить в наш магазин функцию onClick и снова изменить ее на ложный, и наоборот.
Мы почти закончили, но кое-что забыли.
Мы еще не сообщили нашему приложению реакции, что у нас есть глобальное состояние, или, если хотите, что мы используем управление состоянием redux.
Для этого мы заходим в src / index.js, импортируем Provider из response-redux и вновь созданное хранилище следующим образом:
import { Provider } from "react-redux"; import configureStore from "store";
- Provider: делает хранилище Redux доступным для любых вложенных компонентов, которые были заключены в функцию подключения.
После этого вместо прямого рендеринга нашего компонента приложения мы рендерим его через нашего провайдера, используя созданное нами хранилище следующим образом:
ReactDOM.render( <Provider store={configureStore()}> <App /> </Provider>, document.getElementById('root') );
Здесь мы могли бы использовать функцию configureStore с другим состоянием, например configureStore ({rotating: 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();
Давайте посмотрим, работает ли наше приложение redux:
Использование создателей действий
При желании вместо действий мы можем использовать создателей действий, которые представляют собой функции, которые создают действия.
Таким образом, мы можем объединить два наших действия в одну функцию и немного сократить наш код.
Итак, давайте продолжим и создадим новый файл:
1 - команда Linux / Mac
touch src/actions/rotateAction.js
2 - команда Windows
echo "" > src\actions\rotateAction.js
И добавьте этот код:
const rotateAction = (payload) => { return { type: "rotate", payload } } export default rotateAction;
Мы собираемся отправить действие типа rotate с полезной нагрузкой, которую мы собираемся получить в компоненте приложения.
Внутри компонента src / App.js нам нужно импортировать наш новый создатель действий:
import rotateAction from "actions/rotateAction";
Добавьте новую функцию в mapDispatchToProps следующим образом:
rotateAction: получит (полезную нагрузку) и отправит rotateAction с полезной нагрузкой
Измените функцию onClick на:
onClick={() => this.props.rotateAction(!this.props.rotating)}
И, наконец, добавьте нашего нового создателя действий в mapDispatchToProps следующим образом:
rotateAction: (payload) => dispatch(rotateAction(payload))
Мы также можем удалить старый импорт для старых действий, а также удалить их из 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);
Реальный пример с Paper Dashboard React
Как вы увидите на изображении в формате gif выше, я использую правое меню, чтобы изменить цвета меню слева. Это достигается за счет использования состояний компонентов и передачи этого состояния от родительского компонента в два меню и некоторые функции для изменения этого состояния.
Я подумал, что было бы хорошим примером взять этот продукт и заменить состояния компонентов на Redux.
Вы можете получить его тремя способами:
- Скачать с creative-tim.com
- Скачать с Github
- Клон из Github:
git clone https://github.com/creativetimofficial/paper-dashboard-react.git
Теперь, когда у нас есть этот продукт, давайте войдем в него cd и снова установим redux и react-redux:
npm install --save redux react-redux
После этого нам нужно создать действия. Поскольку в правом меню у нас есть 2 цвета, которые задают фон левого меню, и 5 цветов, которые изменяют цвет ссылок, нам нужно 7 действий или 2 создателя действий - и мы выберем этот второй вариант, поскольку он немного меньше кода для написания:
1 - Команды Linux / Mac
mkdir src/actions touch src/actions/setBgAction.js touch src/actions/setColorAction.js
2 - Команды Windows
mkdir src\actions echo "" > src\actions\setBgAction.js echo "" > src\actions\setColorAction.js
После этого создадим следующий код действий:
- src / actions / setBgAction.js
const setBgAction = (payload) => { return { type: "bgChange", payload } } export default setBgAction;
- src / actions / setColorAction.js
const setColorAction = (payload) => { return { type: "colorChange", payload } } export default setColorAction;
Теперь, как и в первой части, нам понадобится редуктор:
1 - Команды Linux / Mac
mkdir src/reducers touch src/reducers/rootReducer.js
2 - Команды Windows
mkdir src\reducers echo "" > src\reducers\rootReducer.js
И код редуктора:
export default (state, action) => { switch (action.type) { case "bgChange": return { ...state, bgColor: action.payload }; case "colorChange": return { ...state, activeColor: action.payload }; default: return state; } };
Как вы можете видеть здесь, в отличие от нашего первого примера, мы хотим сохранить старое состояние и обновить его содержимое.
Еще нам понадобится магазин:
1 - команда Linux / Mac
touch src/store.js
2 - команда Windows
echo "" > src\store.js
Код для этого:
import { createStore } from "redux"; import rootReducer from "reducers/rootReducer"; function configureStore(state = { bgColor: "black", activeColor: "info" }) { return createStore(rootReducer,state); } export default configureStore;
Внутри src / index.js нам понадобятся:
// new imports start import { Provider } from "react-redux"; import configureStore from "store"; // new imports stop
А также измените функцию рендеринга:
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") );
Итак, файл 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") );
Теперь нам нужно внести некоторые изменения в src / layouts / Dashboard / Dashboard.jsx. Нам нужно удалить состояние и функции, которые его изменяют. Так что продолжайте и удалите эти фрагменты кода:
Конструктор (между строками 16 и 22):
constructor(props){ super(props); this.state = { backgroundColor: "black", activeColor: "info", } }
Государственные функции (между строками 41 и 46):
handleActiveClick = (color) => { this.setState({ activeColor: color }); } handleBgClick = (color) => { this.setState({ backgroundColor: color }); }
Свойства боковой панели bgColor и activeColor (строки 53 и 54):
bgColor={this.state.backgroundColor} activeColor={this.state.activeColor}
Все свойства FixedPlugin (между строками 59–62):
bgColor={this.state.backgroundColor} activeColor={this.state.activeColor} handleActiveClick={this.handleActiveClick} handleBgClick={this.handleBgClick}
Итак, мы остаемся с этим кодом внутри компонента макета Dashboard:
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;
Нам нужно подключить к магазину компоненты Боковая панель и FixedPlugin.
Для src / components / Sidebar / Sidebar.jsx:
import { connect } from "react-redux";
И измените экспорт на:
const mapStateToProps = state => ({ ...state }); export default connect(mapStateToProps)(Sidebar);
Для src / components / FixedPlugin / FixedPlugin.jsx:
import { connect } from "react-redux"; import setBgAction from "actions/setBgAction"; import setColorAction from "actions/setColorAction";
И теперь экспорт должен быть:
const mapStateToProps = state => ({ ...state }); const mapDispatchToProps = dispatch => ({ setBgAction: (payload) => dispatch(setBgAction(payload)), setColorAction: (payload) => dispatch(setColorAction(payload)) }); export default connect(mapStateToProps, mapDispatchToProps)(FixedPlugin);
У нас будут следующие изменения:
- где бы вы ни встретили слово 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);
И все готово, можете запустить проект и посмотреть, как все работает нормально:
Несколько редукторов
Поскольку у вас может быть несколько действий, у вас может быть несколько редукторов. Единственное, что вам нужно их объединить - мы увидим это чуть ниже.
Давайте продолжим и создадим два новых редуктора для нашего приложения: один для setBgAction и один для setColorAction:
1 - Команды Linux / Mac
touch src/reducers/bgReducer.js touch src/reducers/colorReducer.js
2 - Команды Windows
echo "" > src\reducers\bgReducer.js echo "" > src\reducers\colorReducer.js
После этого давайте создадим код редукторов следующим образом:
- src / redurs / bgReducer.js
export default (state = {}, action) => { switch (action.type) { case "bgChange": return { ...state, bgColor: action.payload }; default: return state; } };
- src / reducers / colorReducer.js.
export default (state = {} , action) => { switch (action.type) { case "colorChange": return { ...state, activeColor: action.payload }; default: return state; } };
При работе с комбинированными редукторами вам необходимо добавить состояние по умолчанию в каждый из ваших редукторов, которые вы собираетесь объединить. В моем случае я выбрал пустой объект, например state = {};
А теперь наш rootReducer объединит эти два элемента следующим образом:
- src / redurs / rootReducer.js
import { combineReducers } from 'redux'; import bgReducer from 'reducers/bgReducer'; import colorReducer from 'reducers/colorReducer'; export default combineReducers({ activeState: colorReducer, bgState: bgReducer });
Итак, мы говорим, что хотим, чтобы на colorReducer ссылалось свойство activeState состояния приложения, а на bgReducer - bgState - свойство состояния приложения.
Это означает, что наше состояние больше не будет выглядеть так:
state = { activeColor: "color1", bgColor: "color2" }
Теперь это будет выглядеть так:
state = { activeState: { activeColor: "color1" }, bgState: { bgColor: "color2" } }
Поскольку мы изменили наши редукторы, теперь мы объединили их вместе в один, нам также нужно изменить наш 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;
Поскольку мы изменили внешний вид состояния, теперь нам нужно изменить свойства внутри компонентов Боковая панель и FixedPlugin на новый объект состояния:
- src / components / Sidebar / Sidebar.jsx:
Измените строку 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:
Нам нужно изменить все this.props.bgColor
на this.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);
Давайте снова откроем проект с npm start
и посмотрим, как все работает. Да да!
Спасибо за прочтение!
Если вам понравилось читать это руководство, дайте ему аплодисменты. Я очень хочу услышать ваши мысли по этому поводу. Просто оставьте комментарий к этой теме, и я буду более чем счастлив ответить.
Особую благодарность следует также выразить Эстер Фалаи за его руководство, которое дало мне необходимое понимание Redux.
Полезные ссылки:
- Получите код этого руководства с Github
- Узнайте больше о ReactJS на их официальном сайте
- Подробнее о Redux здесь
- Узнать больше о React-Redux
- Посетите нашу платформу, чтобы узнать, что мы делаем и кто мы
- Загрузите Paper Dashboard React с www.creative-tim.com или Github
- Узнайте больше о Reactstrap, ядре Paper Dashboard React.