- PVSM.RU - https://www.pvsm.ru -
Поводом для написания данной статьи явились мои многочисленные наблюдая за разработчиками, которые только начали переходить на React.js с других фреймворков или для которых React.js стал первым фреймворком в его профессиональной деятельности.
Основной вопрос, с которым сталкивается начинающие разработчики на React.js — это что такое state компонента и как его использовать. Многие разработчики в большинстве случаев не правильно его используют, храня в нему кучу ненужных данных, которые впоследствии влияют на вычисления и производительность.
Расскажу о конечном автомате, что это такое, какие реализации существуют и какое отношение конечный автомат имеет к ReactJS.
Хорошее и простое определение конечного автомата можно дать так:
Конечный автомат — это компьютерная программа, которая состоит из:
Конечные автоматы обычно представляют в двух видах:
Автомат состоит из события, к которому привязаны соответствующие обработчики, и состояния, в котором находится автомат.
Для лучшего понимания как работает программа с использованием конечного автомата бывает полезным нарисовать представление ориентированного графа с изображением всех состояний, событий и действий. В другом случаем, может быть полезным перевести автомат в таблицу. В любом случае, надо исходить из конкретной ситуации.
Конечный автомат довольно хорошо применим в много шаговых интерфейсах, где следующий шаг зависит от того, какие параметры были выбраны на текущем (предыдущем) шаге. Например, анкета на оформление кредитной карты. Также, автомат, хорошо применим в интерфейсах, в которых есть логика прогнозирования отрисовки, основанная на выбранных параметрах. Т.е. там где моделируется поведение, при котором реакция на будущие события зависит от предыдущих событий (поэтому автомат хорошо изображается в виде графа).
Machina.js — является одной из реализаций конечного автомата. Подробную документацию можно найти на сайте проекта [1].
Что из себя представляет machina.js:
Чтобы лучше понять, что такое конечный автомат, рассмотрим пример реализации автомата на machina.js (взял код с сайта проекта).
var vehicleSignal = new machina.Fsm( {
// the initialize method is called right after the FSM
// instance is constructed, giving you a place for any
// setup behavior, etc. It receives the same arguments
// (options) as the constructor function.
initialize: function( options ) {
// your setup code goes here...
},
namespace: "vehicle-signal",
// `initialState` tells machina what state to start the FSM in.
// The default value is "uninitialized". Not providing
// this value will throw an exception in v1.0+
initialState: "uninitialized",
// The states object's top level properties are the
// states in which the FSM can exist. Each state object
// contains input handlers for the different inputs
// handled while in that state.
states: {
uninitialized: {
// Input handlers are usually functions. They can
// take arguments, too (even though this one doesn't)
// The "*" handler is special (more on that in a bit)
"*": function() {
this.deferUntilTransition();
// the `transition` method takes a target state (as a string)
// and transitions to it. You should NEVER directly assign the
// state property on an FSM. Also - while it's certainly OK to
// call `transition` externally, you usually end up with the
// cleanest approach if you endeavor to transition *internally*
// and just pass input to the FSM.
this.transition( "green" );
}
},
green: {
// _onEnter is a special handler that is invoked
// immediately as the FSM transitions into the new state
_onEnter: function() {
this.timer = setTimeout( function() {
this.handle( "timeout" );
}.bind( this ), 30000 );
this.emit( "vehicles", { status: GREEN } );
},
// If all you need to do is transition to a new state
// inside an input handler, you can provide the string
// name of the state in place of the input handler function.
timeout: "green-interruptible",
pedestrianWaiting: function() {
this.deferUntilTransition( "green-interruptible" );
},
// _onExit is a special handler that is invoked just before
// the FSM leaves the current state and transitions to another
_onExit: function() {
clearTimeout( this.timer );
}
},
"green-interruptible": {
pedestrianWaiting: "yellow"
},
yellow: {
_onEnter: function() {
this.timer = setTimeout( function() {
this.handle( "timeout" );
}.bind( this ), 5000 );
// machina FSMs are event emitters. Here we're
// emitting a custom event and data, etc.
this.emit( "vehicles", { status: YELLOW } );
},
timeout: "red",
_onExit: function() {
clearTimeout( this.timer );
}
},
red: {
_onEnter: function() {
this.timer = setTimeout( function() {
this.handle( "timeout" );
}.bind( this ), 1000 );
this.emit( "vehicles", { status: RED } );
},
_reset: "green",
_onExit: function() {
clearTimeout(this.timer);
}
}
}
// While you can call the FSM's `handle` method externally, it doesn't
// make for a terribly expressive API. As a general rule, you wrap calls
// to `handle` with more semantically meaningful method calls like these:
reset: function() {
this.handle( "_reset" );
},
pedestrianWaiting: function() {
this.handle( "pedestrianWaiting" );
}
} );
// Now, to use it:
// This call causes the FSM to transition from uninitialized -> green
// & queues up pedestrianWaiting input, which replays after the timeout
// causes a transition to green-interruptible....which immediately
// transitions to yellow since we have a pedestrian waiting. After the
// next timeout, we end up in "red".
vehicleSignal.pedestrianWaiting();
// Once the FSM is in the "red" state, we can reset it to "green" by calling:
vehicleSignal.reset();
Как видно по комментариям в коде, new machina.Fsm — создает экземпляр конечного автомата, в качестве аргумента принимает конфигурацию автомата со всеми его состояниями.
В текущем примере задано 5 состояний автомата, причем автомат может находиться только в одном из этих состояний uninitialized, green, green-interruptible, yellow или red.
Состояние представляет из себя объект, который содержит обработчики текущего состояния.
Таким образом конечный автомат это набор состояний, в каждом из которых он может находится только при выполнении определенных условий.
Подробнее можно почитать на сайте проекта [1], а также посмотреть презентацию [2] и видео [3].
Можно посмотреть на примеры других реализаций конечного автомата:
Думаю, что с конечными автоматами стало уже понятнее, теперь рассмотрим, как они используются в React.js.
Из документации [7] следует, что UI в React.js следует рассматривать конечный автомат.
Таким образом для обновления View необходимо обновить его State и после чего React сам произведет магию перерасчета состояния и перерисует UI.
Обновить состояние View можно используя метод setState({}, [callback]). Данный метод сливает новый state с текущим и после чего вызывается метод render компонента.
Так что должно содержаться в state компонента? Согласно философии React и тому, что компонент представляет из себя конечный автомат, становится очевидным, что в state надо класть только то, что непосредственно отвечает за состояние вашего UI и влияет на его представление, в зависимости от внешних и внутренних условий. Таким образом не надо класть в state компонента все подряд.
Философия [8] React такова, что надо создавать больше stateless компонентов и как можно меньше state компонентов, а в сам state класть минимальный набор данных, которые влияют на представление UI, а для всего остального есть props и Store.
Автор: ekubyshin
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/107781
Ссылки в тексте:
[1] сайте проекта: http://machina-js.org/
[2] презентацию: http://code.dougneiner.com/presentations/machina/#session/1
[3] видео: https://www.youtube.com/watch?v=YW4UB1za3DE
[4] state-manager: https://www.npmjs.com/package/state-manager
[5] state-machine: https://www.npmjs.com/package/state-machine
[6] state.js: https://github.com/steelbreeze/state.js
[7] документации: https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html#components-are-just-state-machines
[8] Философия: https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html#what-shouldnt-go-in-state
[9] Источник: http://habrahabr.ru/post/270673/
Нажмите здесь для печати.