Пробуем React на вкус

Стыдно признаться, но на момент написания этого поста, я так и не разобрался с React.js, поэтому добро пожаловать всем начинающим реактовцам.

Я просто прокомментирую свои действия, возможно они кому-то сэкономят время разбора на старте и поиска необходимых ресурсов.

Ну что ж… зайдем, конечно, на официальный сайт в раздел “Getting Started“. Сразу же есть примеры на фидлере – это хорошо. Посмотрим на первый. OMG! Что это за хрень?!

[javascript]
return <div>Hello {this.props.name}</div>;
[/javascript]

Для тех, кто привык к “обычному” JavaScript такая форма записи покажется более чем странной. Благо дается второй пример, где сделано все тоже, только без черной магии.

Пока мы все еще в шоке от этого синтаксиса, предлагаю посмотреть 18 минут примера работы с React.js:

Попробуем по шагам пройти стартер кит.

Вроде бы основная идея понятна: для черной магии используем атрибут

script type="text/jsx"

и библиотеку, которая будет это парсить:

<script src="build/JSXTransformer.js"></script>

Переходим к туториалу. Вам понадобится сервер, потому хорошо бы сразу скачать себе server.js и положить в корень, после чего выполнить команды:

[shell]
$ npm install express body-parser
$ node server.js
[/shell]

Что почерпнул из туториала:

  • componentDidMount – запускается по умолчанию после того как компонент примонтирован к DOM
  • ref=”author”  – this.refs.author – связь по ref-аттрибуту
  • onSubmit={this.handleSubmit} – Реакт работает с “кэмлКейсом” при определении обработчика события

Я надеюсь у всех завелась эта простая формочка со списком комментариев. Если нет – пишите что не работает в комментариях – я с радостью помогу разобраться.

Далее следуем к разделу “Думаем Реакт сущностями” (Thinking in React).

Резюмируя: разрабатывая приложение на React.js должны действовать в следующеем направлении:

  • разбить UI на компоненты
  • реализовать эти компоненты статически
  • определить минимальный набор стейтов
  • определить где должны находится данные каждого стейта
  • добавить данные

Далее оф сайт предлагает посмотреть видео докладов с конференций. Можете пропустить этот шаг – сэкономите Х часов. Полезной информации для начала работы там практически нет. (Ну так для позитива можно конечно глянуть видео презентации Александра Соловьева)

Вместо этого я рекомендую посмотреть серию видео лекций React Fundamentals на сайте яйцеголового.

Ok, думаю пришло время открыть редактор и что-то написать. Я взял пример из жизни – разработку приложения авторизации для нашего продукта. Так как наш продукт состоит из набора отдельных приложений, а каждое приложение требует авторизации, то здорово было бы вынести эту функциональность в отдельное приложение.

Возьмем заготовку из туториала:

[html]
<html>
<head>
<title>Login Form</title>
<script src="https://fb.me/react-0.13.3.js"></script>
<script src="https://fb.me/JSXTransformer-0.13.3.js"></script>
<script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx" src="app.js"></script>
</body>
</html>
[/html]

Теперь посмотрим какие компоненты у нас есть:

studytube_login

Пока что явно вырисовывается только один компонент – форма логина. Ок добавим для нее Реакт-класс и создадим статический элемент:

[javascript]
var LoginForm = React.createClass({
render: function() {
return (
<form className="loginForm">
<br/>email:<input type="text" name="email" ref="email"/>
<br/>Password:<input type="password" name="password" ref="password"/>
<br/><input type="submit" value="Login"/>
</form>
);
}
});

React.render(<LoginForm/>, document.getElementById(‘content’));
[/javascript]

Не забываем про ref атрибут, чтобы потом можно было удобно получить доступ к данным.

Вот что у нас получилось:

login react

пока не густо. Перенесем стили и маркап со старого приложения. Тут нужно не забыть, что в реакт-компонентах мы прописываем не class атрибут, а className. Плюс закрываем все теги, да, и input тоже, для JSX это важно. Получили статическую версию со стилями:

studytube react login

Теперь добавим первое действие – сабмит формы:

[javascript]
<form className="form" onSubmit={this.handleSubmit}>
[/javascript]

handleSubmit, как вы уже знаете из туториала, – наш хендлер сабмита, который будет отправлять данные:

[javascript]
handleSubmit: function(e) {
e.preventDefault();

var loginData = {};
loginData.email = React.findDOMNode(this.refs.email).value.trim();
loginData.password = React.findDOMNode(this.refs.password).value.trim();
loginData.academyId = 1;

$.post(‘http://localhost:3000/auth’, loginData,function(result){
console.log(result);
});
}
[/javascript]

На сервере пока просто заглушка:

[javascript]
app.post(‘/auth’, function(req, res) {
if(req.body.email === ‘xxx’ && req.body.password === ‘xxx’){
var data = true;
} else {
data = false;
}
res.send(JSON.stringify(data));
});
[/javascript]

Время подумать о реакции UI на ответ сервера. Для начала разберем вариант не верных логина/пароля. Мы хотим подсветить поля формы красным и вывести сообщение.

Опишем наш стейт и определим в нем свойство отвечающее за правильность данных формы:

[javascript]
getInitialState: function() {
return {
isValid: true
};
},
[/javascript]

Теперь если запрос вернет ошибку:

[javascript]
var self = this;
$.post(‘http://localhost:3000/auth’, loginData,function(result){
if(result !== ‘true’){ //очень грубо сделано только для примера
self.setState({isValid: false});
}
});
[/javascript]

И пропишем в рендер-методе, что делать при разных значениях свойства стейта:

[javascript]
var errorClass = this.state.isValid?”:’input–error’;
[/javascript]

[html]
<input type="text" className={"input input–full " + errorClass} name="email" ref="email"/>
[/html]

И еще круто было бы не только менять класс, чтобы как-то выделить поле с ошибкой, но еще и выводить сообщение:

[html]
<span className="input-decorator__errormsg">Invalid email…</span>
[/html]

Сообщение об ошибке – это хороший кандидат, чтобы стать отдельным компонентом. Так и сделаем:

[javascript]
var ErrorMessageInput = React.createClass({
render: function(){
return <span className="input-decorator__errormsg">{this.props.text}</span>
}
});
[/javascript]

и теперь в нашем основном элементе:

[javascript]
{!this.state.isValid?<ErrorMessageInput text="Invalid email…"/>:null}
[/javascript]

Сделаем так, чтобы при изменении поля ошибки сбрасывались. Для этого повесим обработчики на поля ввода:

[html]
<input type="text" className={"input input–full " + errorClass} ref="email" onChange={this.handleChange}/>

<input type="text" className={"input input–full " + errorClass} ref="password" onChange={this.handleChange}/>
[/html]

и пропишем саму функцию обработчик:

[javascript]
handleChange: function() {
this.setState({ isValid: true });
},
[/javascript]

Что еще можно улучшить? Давайте сделаем кнопку логин не активной, когда не все поля введены. Для этого добавим в стейт наши поля:

[javascript]
getInitialState: function() {
return {
email: ”,
password: ”,
isValid: true
};
},
[/javascript]

и теперь при изменении поля мы должны обновить стейт, добавим в наш handleChange метод:

[javascript]
handleChange: function() {
this.setState(
{
isValid: true,
email: React.findDOMNode(this.refs.email).value,
password: React.findDOMNode(this.refs.password).value
});
},
[/javascript]

и добавить атрибут, который будет рендериться по условию:

[html]
<button disabled={!this.state.email || !this.state.password}>Let me in</button>
[/html]

Итоговый код компонента:

[javascript]
var LoginForm = React.createClass({
getInitialState: function() {
return {
email: ”,
password: ”,
isValid: true
};
},
handleChange: function() {
this.setState(
{
isValid: true,
email: React.findDOMNode(this.refs.email).value,
password: React.findDOMNode(this.refs.password).value
});
},
handleSubmit: function(e) {
e.preventDefault();

//your code to communicate with server
//…
//just to test
self.setState({ isValid: false });
},
render: function() {
var errorClass = this.state.isValid ? ” : ‘input–error’;

return (
<div className="loginForm solo-form solo-form__page">
<div className="l-block">
<div className="sf-header">
<div className="sf-header__title">Log in</div>
</div>
</div>
<div className="solo-form__form">
<form className="form" onSubmit={this.handleSubmit}>
<div className="l-block-small">
<label className="input-decorator">
<input type="text" className={"input input–full " + errorClass} value={this.state.email} name="email"
ref="email" onChange={this.handleChange}/>

<div className="input-decorator__label">Email address</div>
{!this.state.isValid ? <ErrorMessageInput text="Invalid email…"/> : null}
</label>
</div>

<div className="l-block-small">
<label className="input-decorator">
<input type="password" className={"input input–full " + errorClass} value={this.state.password}
name="password" ref="password" onChange={this.handleChange}/>

<div className="input-decorator__label">Password</div>
{!this.state.isValid ? <ErrorMessageInput text="… or password"/> : null}
</label>
</div>

<div className="l-block-med is-center-aligned">
<a href="/restore-password" className="form__action-secondary">Forgot password?</a>
</div>

<div className="l-block-med">
<button className="button button–primary button–full"
disabled={!this.state.email || !this.state.password || !this.state.isValid}>Let me in
</button>
</div>
</form>
</div>
</div>
);
}
});
[/javascript]

 

Что еще полезного почитать?