Знакомство с React.js, React Router, Redux, Webpack за 7 шагов
Помните, ключевой способ достичь успеха — это метод проб и ошибок. (с) Томас Эдисон
Для кого эта статья?
Эта статья — руководство для тех, кто уже знаком с JavaScript. Однако глубокое знание языка не является обязательным условием для её прочтения. С помощью этой статьи вы сможете собрать каркас будущего приложения и использовать его в своих проектах.
Шаг 1 — package.json
Для начала потребуется создать папку проекта — mkdir <название_проекта>
(mkdir lesson). Переходим в неё с помощью команды cd /название_диска/название_проекта/
(cd /c/lesson).
Далее вводим команду npm init
(автоматически создается файл package.json).
Отвечаем на вопросы:
package name — по умолчанию название корневой папки, нажимаем Enter;
version — версия приложения, нажимаем Enter;
description — описание приложения;
entry point — входной файл по умолчанию index.js, нажимаем Enter;
test command — тестирование сборки, нажимаем Enter;
git repository — ссылка на ваш репозиторий;
keywords — ключевые слова для поиска приложения;
author — имя автора или команды;
license — (ISC) - свободная лицензия для ПО, нажимаем Enter;
Is this OK? (yes)- вводим yes, и package.json создан.
Шаг 2 — Установка React.js
Заходим в корень проекта cd /имя_диска/название_проекта
и в командной строке вводим npm install --save react
(появится папка node_modules и файл package-lock.json).
node_modules — в нем содержатся все установленные пакеты;
package-lock.json используется для блокировки зависимостей от определенного номера версии.
Ура! React.js установлен))
Документация ReactJS
Шаг 3 — .gitignore
Создаем файл с названием .gitignore в корне проекта.
Внутри указываем список папок, файлов, которые не должны попасть на git.
/node_modules
/dist — production сборка
/.idea — указать, если используете WebStorm
Шаг 4 — Установка Webpack, Babel
Заходим в корень проекта cd /название_диска/название_проекта/
и устанавливаем необходимые пакеты:
npm install --save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin @babel/core @babel/plugin-transform-runtime @babel/preset-env @babel/preset-react @babel/runtime babel-loader babel-plugin-transform-class-propertieswebpack-cli — https://www.npmjs.com/package/webpack-cli ;
webpack-dev-server — сервер Node.js;
html-webpack-plugin упрощает создание HTML-файлов;
babel — JavaScript-компилятор.
В корне проекта создаем файл webpack.config.js (пример кода), читаем комментарии. Если будете копировать, то комментарии нужно удалить.
Также в корне проекта создаем файл .babelrc.
.babelrc
________{
"presets": ["@ babel / preset-env", "@ babel / preset-response"],
"plugins": ["transform-class-properties", "@ babel / plugin-transform-runtime"]
}// Комментарии не копируем
// Используем @babel/preset-env для того, чтобы применять только те плагины, которые необходимы для нашего проекта;
// @babel/preset-react преобразовывает в JavaScript и JSX-код;
// transform-class-properties — https://www.npmjs.com/package/babel-plugin-transform-class-properties ;
// @babel/plugin-transform-runtime — плагин, который позволяет повторно использовать внедренный вспомогательный код Babel
для экономии на размере кода.код GitHub
В package.json, в блоке script: {}
добавим 2 строчки:
"start": "webpack-dev-server --mode development --open --hot --progress --colors --config ./webpack.config.js",
// запуск нашего приложения;
"build": "webpack --mode production"
// production сборка
Шаг 5 — Создание корневого каталога и запуск приложения
Заходим в корень проекта cd /название_диска/название_проекта
.
Создаем папку mkdir public
и переходим в неё cd /public
, внутри создаем файл index.html (печатаем !
, нажимаем Tab). Открываем файл index.html, затем внутри тега <body>
добавляем <div id="root"></div>
(пример кода).
В корне проекта создаем папку mkdir src
и переходим в неё cd src
.
Внутри создаем папку mkdir components
и переходим в неё cd components
.
Внутри создаем папку mkdir App
и переходим в неё cd App
, внутри создаем файл index.js.
src > components > App > index.js
_________________________________import React, { Component } from "react";export default class App extends Component {
render () {
const {children} = this.props;
return (
<div>
{children}
</ div>
);
}
}код github
В корне папки src
создаем входной файл index.js, код.
src > index.js
______________import React from "react";
import { render } from 'react-dom';
import App from './components/App';render (
<App>
Hello World!
</ App>,
document.getElementById ("root")
);код github
Запускаем приложение командой npm start
, получаем ошибку. Устанавливаем пакет react-dom npm install --save-dev react-dom
и снова запускаем приложение.
react-dom предоставляет методы, специфичные для DOM, которые можно использовать на верхнем уровне вашего приложения и в качестве аварийного люка, чтобы выйти за пределы модели React, если это необходимо.
Шаг 6 — React Router
Заходим в корень проекта cd /название_диска/название_проекта/
и устанавливаем необходимый пакет:
установка npm --save-dev реагирует-маршрутизатор-домreact-router-dom — роутинг для приложения.
Переходим в папку сomponents cd src/components
, затем внутри создаем следующие папки mkdir Header Main Home Roster
.
Header — в ней мы будем хранить основную навигацию для нашего приложения;
Main — в ней мы будем хранить группу роутов;
Home и Roster — в них будет информация.
Внутри каждой папки создаем файл index.js.
Header > index.js
/*...*/
import { Link } from "react-router-dom";/*...*/
<Link to="/">Home</Link>
/*...*/
полный код на github
Для перехода между компонентами будем использовать <Link to="/">
, а не <a href=""/>
.
Почему?<a href=""/>
— перезагружает страницу полностью, что нам не подходит. Необходимо перерисовать (перерендерить) только вызываемый компонент, поэтому мы будем использовать <Link to="/">
.<Link to="/">
— не перерисовывает и не перезагружает страницу полностью, а перерисовывает только вызванный компонент.
Main > index.js
/*...*/
import { Switch, Route } from "react-router-dom";/*...*/
<Switch>
<Route exact path='/' component={Home}/>
/*...*/
</Switch>
/*...*/
полный код на github
<Switch />
— используется для группировки роутов;<Route path='/' component={/*...*/} />
— рендер пользовательского интерфейса, который мы передаем через component={название_компонента_который_необходимо_рендерить}
.
Home > index.js
import React, { Fragment } from "react"; const Home = () => <Fragment>Home</Fragment>; export default Home;код на github
Roster > index.js
import React, { Fragment } from "react"; const Roster = () => <Fragment>Roster</Fragment>; export default Roster;код на github
Переходим в папку cd src
, открываем файл index.js:
/*...*/
import {BrowserRouter as Router} from "react-router-dom";render(
<Router>
<App>
Hello World!
</App>
</Router>,
document.getElementById("root")
);
HashRouter используем, когда у нас статический веб-сайт;
BrowserRouter следует использовать, когда обрабатываются динамические запросы на сервере.
Переходим в папку cd components/App
, открываем файл index.js
/*...*/
import Header from "../Header";
import Main from "../Main";/*...*/
<Header />
<Main />
/*...*/полный код на github
Запускаем приложение командой npm start
, теперь мы можем вызывать разные компоненты по нажатию на ссылки Home или Roster.
Есть маленький нюанс: если обновить страницу, например, страницу /roster, будет получена ошибка cannot GET, которую легко исправить.
Открываем в корне проекта файл webpack.config.js, находим на 25 строчке devServer, добавляем historyApiFallback: true
и проверяем в браузере.
webpack.config.js/*...*/
devServer: {
historyApiFallback: true,
/*...*/
}
/*...*/полный код на github
Шаг 7 — Redux
Заходим в корень проекта cd /название_диска/название_проекта/
и устанавливаем необходимые пакеты:
npm install --save redux react-redux redux-thunk redux-logger connected-react-router historyredux — библиотека состояния для вашего приложения;
react-redux — официальная версия redux для react;
redux-thunk — позволяет писать создатели действий, которые возвращают функцию вместо самого действия;
redux-logger — библиотека, которая показывает все действия приложения, связанные с redux, и выводит их на консоль;
connected-react-router — библиотека, которая синхронизирует маршруты с состоянием;
history — библиотека, которая позволяет легко управлять историей сеансов.
Есть несколько основных принципов в Redux, которые нужно понимать:
Есть 1 глобальный объект состояния, который управляет состоянием всего приложения. В этом примере он будет идентичен начальному состоянию компонента. Это единственный источник истины.
Единственный способ изменить состояние — это отправка действий. Действия представляют собой объекты описывающие то, что должно измениться. Создатели действий (Action Creators) — это функции, которые могут быть отправлены (dispatched). Всё что они делают это возвращают действие.
Когда действие отправлено, редьюсер (функция) либо изменяет состояние в соответствии с отправленным действием, либо возвращает текущее состояние если действие не применимо к редьюсеру.
Редьюсеры — это “чистые функции”. Они не должны изменять состояние, вместо этого они должны возвращать модифицированную копию.
Индивидуальные редьюсеры объединены в один корневой редьюсер (rootReducer) для создания отдельных свойств состояния.
Хранилище (store) — это такая штука, которая всё это объединяет: оно представляет состояние используя корневой редьюсер, какие-то промежуточные слои (middleware, в нашем случае Thunk), и позволяет фактически отправлять действия.
Для того чтобы использовать Redux вместе с React существует компонент , который оборачивает всё наше приложение и передаёт хранилище store всем дочерним элементам.
Переходим в папку cd /src
и создаем папку mkdir redux
. Переходим в неё cd /redux
и создаем следующие папки mkdir actions reducers stores
.
actions
В actions мы будем хранить действия. Переходим в папку actions и создаем файл index.js, в котором будем перечислять действия.
actions > index.js
__________________import { getRoster } from "./rosterActions";
// импортируем действие, а также другие ниже export { getRoster };
// перечисляем действия и теперь,
// когда нам нужно будет вызвать действие внутри компонента,
// делаем это так:
// import { getRoster, /*и др.*/ } from "../../redux/actions";код на github
Создаем файл действия rosterActions.js.
actions > rosterActions.js
__________________________export const getRoster = () => dispatch => {
dispatch({ type: "GET_ROSTER_REQ" });
};код на github
reducers
В reducers будем хранить “чистые функции”, которые вернут измененное состояние. Переходим в папку reducers и создаем файл index.js, в котором будем комбинировать/объединять редьюсеры;
reducers > index.js
___________________import { combineReducers } from "redux";
import { connectRouter } from "connected-react-router";
import roster from "./roster"; export default history =>
combineReducers({
router: connectRouter(history),
roster
});код на github
Создаем наш первый редьюсер roster.js.
reducers > roster.js
____________________const initialState = {
rosterLoader: false
}; const roster = (state = initialState, action) => {
if (action.type === "GET_ROSTER_REQ") {
return {
...state,
rosterLoader: !state.rosterLoader
};
}
return state;
};export default roster;код на github
stores
В stores храним настройки хранилища (store). Переходим в папку stores и создаем файл configureStore.js — это хранилище.
stores > configureStore.js
__________________________import { createBrowserHistory } from "history";
import { createStore, applyMiddleware } from "redux";
import { createLogger } from "redux-logger";
import thunk from "redux-thunk";
import { routerMiddleware } from "connected-react-router";
import rootReducer from "../reducers"; const logger = createLogger();export const history = createBrowserHistory();
const createStoreWithMiddleware = applyMiddleware(
thunk,
routerMiddleware(history),
logger)
(createStore);export default function configureStore(initialState) {
return createStoreWithMiddleware(rootReducer(history), initialState);
}код на github
Переходим в папку src и открываем файл index.js
- Импортируем компонент
<Provider />
, он содержится в react-redux. Provider принимает свойство store, в которое мы передаем хранилище configureStore.js, и затем все дочерние элементы смогут его использовать. - history поможет получить историю сеансов.
Для того, чтобы синхронизировать историю сеансов и состояние, нам нужно Router заменить на ConnectedRouter — import { ConnectedRouter } from "connected-react-router";
.
src > index.js
______________/*...*/
import { Provider } from "react-redux";
import configureStore, { history } from "./redux/stores/configureStore";
/*...*/
import { ConnectedRouter } from "connected-react-router";const store = configureStore(); render(
<Provider store={store}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</Provider>,
document.getElementById("root")
);код на github
Также изменяем наш компонент Roster, переходим в cd /components/Roster/index.js
.
src > components > Roster > index.js
____________________________________import React from "react";
import { connect } from 'react-redux';
import { getRoster } from "../../redux/actions";
import Roster from "./presenter"; const mapStateToProps = state => {
return {
state,
roster: state.roster.rosterLoader
};
}; const mapDispatchToProps = dispatch => ({
getRoster: () => dispatch(getRoster()),
});export default connect(
mapStateToProps,
mapDispatchToProps
)(Roster);код на github
Создаем в корне Roster новый файл presenter.js.
src > components > Roster > presenter.js
________________________________________import React, { Component, Fragment } from "react";class Roster extends Component {
render () {
const {roster, getRoster} = this.props;
return (
<Fragment>
Roster
<button onClick = {getRoster}>
{roster ? "true": "false"}
</ button>
</ Fragment>
);
}
}export default Roster;код github
Пересобираем проект и смотрим, что получилось.
В консоли будут отображаться действия после нажатия на кнопку.
У переменной roster после каждого нажатия будет изменяться значение. Засчет этого, можно контролировать поведение элементов, обращения к api и многое другое.
Переменная доступна везде и хранит измененное состояние вплоть до полной перезагрузки страницы, именно поэтому мы и использовали для переходов между компонентами компонент <Link to=""/>
, а не тег ссылки <a href=""/>
.
Например, передадим переменную roster не только в компонент Roster, но и в компонент Home. Для того, чтобы передать переменную, необходимо модифицировать компонент Home, так же, как мы это сделали с компонентом Roster.
Добавим точно такое же условие и заменим получаемые значения ‘true’ на ‘Home’ и ‘false’ на ‘No Home’, соответственно. Когда будет нажата кнопка в компоненте Roster, и ‘true’ сменится на ‘false’, перейдем на страницу компонента Home и увидим, что ‘Home’ сменилось на ‘No Home’.
Официальная документация Redux
Перевод официальной документации по Redux
Когда мы создавали файл .gitignore, то указывали папку /dist. Она создается, если у нас есть production сборка.
После ввода команды npm run build
начинается компиляция, и в структуре проекта появляется новая папка с названием /dist.
Данная сборка запускается только на сервере. Чтобы проверить локально, установим пакет serve глобально npm install serve -g
. Переходим в папку cd dist
и вводим команду serve
. Затем переходим по ссылке, которую предоставит пакет serve. Готово! Теперь у вас есть production сборка!
Успех составляет только 1%, а 99% — неудачи.(с) Соитиро Хонда