Блог на ReactJS #2. Несколько компонентов, props, state. npm run, конфигурация webpack

Этот урок входит в цикл "Блог на React". Из прошлого урока мы узнали как собирать своё React-приложение. В данном уроке мы углубимся в разработку компонентов, поработаем со state и props. У нас уже есть начальное приложение (если нет, создайте по предыдущему уроку или по примеру в jsfiddle) Напоминаю, что цель этой серии уроков - сделать блог. Для своего блога я выбрал тему - Африка. Зачем нужны state, props? state и props - особые переменные в компоненте. Эти переменные отслеживаются реактом, и, в случае изменения, компонент перерисовывается (вызывается метод render). Когда использовать props? Веб-приложение на React использует модульную структуру. Каждый компонент изолирован от других компонентов и не может взаимодействовать с ними напрямую. Это помогает уменьшить связываемость кода, увеличивает количество повторно используемого кода и облегчает тестирование. Но как тогда передать из родительского компонента в дочерний информацию о том, что ему отображать? Для этого используются props (properties) - свойства. Давайте разберём на примере. В наш main.js добавим компонент Head (со свойствами children, color): main.jsx
1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import ReactDOM from 'react-dom';
import Head from './head.jsx';
   
class Main extends React.PureComponent {
  render() {
    return <Head color="red">Hey, Africa!</Head>;
  }
}
  
ReactDOM.render(<Main />, document.getElementById('main'));
head.jsx
1
2
3
4
5
6
7
8
9
import React from 'react';
 
const Head = props => (
  <div style={{ color: props.color }}>
    {props.children}
  </div>
);
 
export default Head;
На экран выведется "Hey, Africa!" Т.к. в children хранятся вложенные HTML-элементы и другие React-компоненты. children - зарезервированное свойство в props обозначающее вложенные объекты. Передавать в него эти объекты можно просто указывая их внутри тега компонента (например: <Div>asd</Div> - в компоненте Div, children это asd) Но также надпись будет красного цвета. Потому что мы передали дополнительное свойство в props компонента Head color = "red". Затем принименили в style к возвращаемому div'у.
Зачем нужен state? state - схож с props, с той разницей, что это внутреннее состояние компонента(как понятно из названия). state нельзя поменять извне компонента. Т.е. state, как и props, нужен для отслеживания изменений, но внутренних. Костяк приложения у нас есть, нам нужно лишь добавить обработку стилей. Добавляем стили в формате SCSS (он нам упростит указание вложенных элементов): styles.scss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
* {
  font-family: Lato, sans-serif;
  padding: 0;
  margin: 0;
}
 
#main {
  background: #eee;
   
  .head {
    padding: 20px;
    background: #d00;
    color: #fff;
  }
   
  .item {
    margin: 8px 0;
    background: #fff;
    padding: 20px;
  }
}
 
@font-face {
    font-family: Lato;
    src: local('Lato'),
}
Подправим main.js - уберём передачу color и получение их из props в head.js: head.js
1
2
3
const Head = props => (
   <h1 className="head">{props.children}</h1>
);
Для обработки стилей добавим загрузчик CSS, SASS/SCSS и плагин для сборки в один файл в webpack: npm install sass-loader css-loader style-loader node-sass extract-text-webpack-plugin --save-dev webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
 
module.exports = {
  entry: [
    './main.jsx',
    './styles.scss'
  ],
  output: {path: __dirname, filename: 'bundle.js'},
  module: {
    loaders: [
      {
        test: /.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        query: {
          presets: ['es2015', 'react']
        }
      },
      {
        test: /\.scss$/,
        loader: ExtractTextPlugin.extract('css-loader!sass-loader')
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin('bundle.css', {allChunks: true})
  ]
};
Также не помешает создать более удобные команды для сборки проекта. Для этого используем npm run. В package.json - конфиг npm добавим команду serve в scripts package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
  "scripts": {
    "serve": "webpack-dev-server --watch",
    "build": "webpack"
  },
  "dependencies": {
    "react": "^16.0.0",
    "react-dom": "^16.0.0"
  },
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "css-loader": "^0.28.7",
    "extract-text-webpack-plugin": "^3.0.2",
    "node-sass": "^4.5.3",
    "sass-loader": "^6.0.6",
    "style-loader": "^0.19.0",
    "webpack": "^3.8.1",
    "webpack-dev-server": "^2.9.3"
  }
}
Теперь для запуска webpack-dev-server --watch достаточно ввести:: npm run serve Для сборбки bundle-файлов: npm run build Теперь SCSS собирается в bundle.css Последний шаг - нужно подключить bundle.css к нашему index.html и механизм сборки будет полностью налажен и автоматизирован index.html
1
2
3
4
5
6
7
8
9
10
11
12
<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hello React</title>
  <link rel="stylesheet" href="bundle.css">
</head>
<body>
<div id="main"></div>
  <script src="bundle.js"></script>
</body>
</html>
На этом настройка окружения закончена. SCSS, JSX компилируются в CSS, JS соответственно!
Примите поздравления с её окончанием! До завершения проекта остаётся ещё немного настроек, но уже не окружения, а взаимодействия компонентов между собой, будем использовать Redux. И дальше будет только реализация новых фич и чистое программирование. Следующая статья будет посвящена Redux, Mock-серверу (json-server) и получению/сохранению/отображению данных с бэкенда. Спасибо за внимание.
Если статья Вам показалась незаконченной или Вы знаете как её улучшить, пожалуйста сообщите мне e@gohtml.ru