How to use model in a React Application

D: “Model has to be some where, where do I put the model?”

E: “At the top, the model is always at the top most. But I do not like react”

This conversation with https://twitter.com/emanuele_r (https://github.com/lele85 Emanuele Rampichini) take place in late november
during a code retreat, but it was off topic, because I does not put into test React framework.

What make me trouble was the way React component class use properties (this.props in the class) and state (this.state and this.setState() method),
there are no “legal” method to change status of a class from outside that class (of course it is for encapsulation), but
also there is no methods to notify or sending a message to the class (apparently).

I have been busy on php coding, some week ago I found a viable solution, and after using it I think it is time to share.

There are a number of reason to doing so, i.e. http://stackoverflow.com/questions/25336124/reactjs-how-to-modify-child-state-or-props-from-parent/27720180
that was inspiring me, I replyed
http://stackoverflow.com/questions/25336124/reactjs-how-to-modify-child-state-or-props-from-parent/27720180#27720180
why do not use observer pattern?

But wait a moment, observer pattern has the reputation in Javascript as eventemitter, instead of reinvent the wheel I can inherit from
a ready package, say https://github.com/asyncly/EventEmitter2

var EventEmitter = require('./eventemitter2.js');

function Model() {
  EventEmitter.apply(this);
  this.choosen = ''
  this.options = ['a','b','c'];
};

Model.prototype = Object.create(EventEmitter.prototype);
Model.prototype.constructor = Model;

I have not parameter for my model, and there is actually no reason to change event emitter parameter changable from the outside

Then proceed with Model’s methods:

Model.prototype.chooseOption = function(optn) {
  this.choosen = optn;
  this.emit('optionChange')
};

Model.prototype.getOption = function() {
  return this.options;
};

Now use it in the class:

var model = new require('./Model.js')();

var MyApp = React.createClass({
   updateSelected: function() {
      var state = this.state
      state.choosen = model.choosen
      this.setState(state)
   },
   componentDidMount: function() {
      model.on('optionChange',this.updateSelected);
   },
   getInitialState:function() {
      return {choosen:model.choosen};
   },
   render: function() {
      return (
            <div>
		<strong>{this.state.choosen}</strong>
		<ExtraComponent name="taskone" model={model} />
            </div>
            );
   }
});

Now, ExtraComponent can use
this.props.model.on(this.props.name + ‘Change’, this.updateState);

in componentDidMount to listen to events from model, and also emit event based in its name, say calling

this.props.model.emitMyEvents(this.props.name)

in updateState:

var ExtraComponent = React.createClass({
   updateState: function() {
      var state = this.state
      state.isSaved = this.props.model.saved()
      this.setState(state)
      this.props.model.emitMyEvents(this.props.name)
   },
   componentDidMount: function() {
      model.on(this.props.name+'Change',this.updateState);
   },
   getInitialState:function() {
      return {choosen:this.props.model.isSaved};
   },
   render: function() {
      var title = this.props.model.getTitle(this.props.name);
      var content = this.props.model.getContent(this.props.name);
      return (
            <div>
		<h3>{title}</h3>
		<p>{content}</p>
            </div>
            );
   }
});

Here there is emitMyEvents(name) and is responsibility of the model to know which events should emit the component named name.

This will work as a agnostic component and its behavior is driven by the model. A good example of such a component is a
collapsing accordion that could be driven from outside and is agnostic of the behaviour of whole application.

Model.prototype.emitMyEvents = function(name) {
   if(name == 'taskone') {
      this.emit('accordionxUnvisible');
      this.emit('accordionTwoUnvisible');
      this.emit('accordionStrangeVisible');
   }
   // other emitions
};

 … maybe emitEventsByName would be more talking method name.

This code works? almost, this is an example, and I have not tested the code, some parts are clearly missing, I am using similar code for an application I am writing now, no problem.

I found this approach is elestic and almost free, it rely on a design pattern and thus is solid.

But there is some point to take care of:

  1. passing responsibilities to the outer model
  2. making component agnostic require an extra efford: give them a name and implement resposibility migration mechanism

 


Posted

in

,

by

Tags: