Alternating states of a js application

Asked

Viewed 126 times

4

INTRODUCTION

I’m developing an app using Electron and google maps, I need to control the state of one of the windows, for example: the user clicks the button and from this moment on all click on the map adds a marker.

Well the problem is in controlling the states, I tried to come up with some solution, I went after tutorials, libraries like React and/ or frameworks like angular 1, 2, also falls into a frame work called Choo but in the end I did not reach any conclusion, I just got more confused on how to do.

MY SOLUTION

To begin with, I will present my first attempt to ' solution' based on this TUTORIAL, in which I made some changes.

function machine () {
  this.estadoatual = undefined;
  const estados = {
    estado1:  {
      log:() => {console.log('teste')}
      /*funçoes e comandos do estado*/
    },
    estado2:  {
      log:() => {console.log('teste2')}
      /*funçoes e comandos do estado*/
    }
    /*...*/
  };

  this.changeState = (string) => {
    //verifica a string e o estado atual
    if ( string == 'estado1' && this.estadoatual !== estados.estado1) {
      this.estadoatual = estados.estado1;
    }
    //verifica a string e o estado atual
    if ( string == 'estado2' && this.estadoatual !== estados.estado2) {
      this.estadoatual = estados.estado2;
    }
  }
  
  
}

const teste = new machine();

/* por favor abra o console :P */
teste.changeState('estado1');
teste.estadoatual.log();
teste.changeState('estado2');
teste.estadoatual.log();

In this solution all states would have to have the same properties to be executed correctly, but this would result in a lot of other functions resulting from each object, such as: test.estadoatual.render(), test.estadoatual.deleteRender() and so on, with this amount of functions it’s all more confusing.

I thought of a second solution to my problem:

function machine () {
  this.estadoatual = undefined; 
  const self = this;
  
  function estado1 () {    
    console.log('executou o estado1');
    function log () {console.log('click estado1')};// função chamada no evento de click bt1    
    $('#bt1').bind('click', log);
    $('#bt2').bind('click', (event) => {
    self.changestate('estado2');
    $('#bt1').unbind('click', log);
    $(this).unbind(event);  
    })
    this.log = () => {console.log('estado1')}; 
  }
  
  function estado2 () {
    console.log('executou o estado2');
    function log () {console.log('click estado2')};// função chamada no evento de click bt1    
    $('#bt1').bind('click', log);
    $('#bt2').bind('click', (event) => {
    self.changestate('estado1');
    $('#bt1').unbind('click', log);
    $(this).unbind(event);  
    })
    this.log = () => {console.log('estado2')}; 
  }  
  
  this.changestate = (string) => {
   if(string == 'estado1')  { 
/* neste caso não sei como fazer a verificação para não reiniciar o valor do 'estadoatual' quando o usuario clicar de novo*/
     this.estadoatual = new estado1();     
     }
      if(string == 'estado2')  {
/* neste caso não sei como fazer a verificação para não reiniciar o valor do 'estadoatual' quando o usuario clicar de novo*/
     this.estadoatual = new estado2();       
     }
  }
}

const teste = new machine();

//Inicializa o app no estado1
//neste caso ele é quase todo contido em si , não necessitando de chamadas externas, mas também não descartando elas.
teste.changestate('estado1');
//consigo chamar funções que podem ser acessadas fora do escopo da função.
teste.estadoatual.log();
.bt{
height: 30px;
 width: 50px;  
}

#bt1{
background-color: red;
}


#bt2{
background-color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id='bt1' class='bt'></div>
<div id='bt2' class='bt'></div>

The above code works somehow ... well, the red button runs the log, and the blue change status, but I can’t verify which state it is to prevent the user from being "spammed" the button and the app bugges, which would be bad, in which case it doesn’t happen but when it receives a command from a button that is contained in another window, the user can simply keep pressing and re-starting the code.

DOUBTS

EDIT: The questions, are in a way individual, I do not know if something like this is allowed, but if you have the answer to any of them, please share with us

  1. How can I check the status in the second example? Something similar to checking the first solution. <<< SOLVED (READ REPLY AND EDITS).
  2. Are there alternatives to this design? If so, which ones do you know and which ones you recommend?
  3. With my research, I saw that frameworks as angular and Choo deal with the state of the application, there are more alternatives to them?
  4. What is the advantage of using frameworks instead of the method I’m using?
  5. React also handles states, but what difference do they make to angular or Choo?

BIBLIOGRAPHY

Tutorial state Pattern: http://robdodson.me/take-control-of-your-app-with-the-javascript-state-patten/ Choo: https://github.com/yoshuawuyts/choo
Angular: https://angular.io/
React: https://facebook.github.io/react/
Electron:http://electron.atom.io/

EDITS

After a little more effort, I came to an answer that reacts as I wish, follows the answer:

function machine() {
  const self = this;
  
  this.estadoatual = undefined;
  let estadoatual = undefined;
  let estados = [];


  this.addstate = (obj) => {
  	estados.push(obj);
    console.log('[STATE_MACHINE]> '+ obj.name + ' <added to states!');
  };

  this.changestate = (string) => {
	for(var i in estados) {
    	if(string == estados[i].name) {
          console.log('[STATE_MACHINE] Found: ' + estados[i].name);
          if(string !== estadoatual){
       	    console.log('[STATE_MACHINE] State changed to: ' + estados[i].name + '.');
       	    estadoatual = estados[i].name;
       	    self.estadoatual = new estados[i].constructor;
       }
      }
    }
  }

}    

const sm = new machine();
//Note que o objeto leva somente dois parametros, na função construtora, pode se escrever o que desejar.
sm.addstate({name:'nome', constructor: function(){
  console.log('teste');
}});

sm.changestate('nome');             

  • This theme is very interesting but your question is 4 in 1 and so it is difficult to answer... Can you "break it" in different questions? Or one after the other to have a sequel maybe...

  • Well... the sequence would be more or less the one in the doubt part :P but I can try to define them better

  • thanks :P I am a good days behind the answer...

  • Hi Joao. Can you post your answer below, in the area of answers? It became strange to have the answer as part of the question.

  • after I finish making some adjustments I put yes

  • @Don’t forget to put the answer below :)

Show 1 more comment

1 answer

0


REPLY:

After some effort, I arrived at a definitive answer, now I also know that there are many other ways to get this result and ways to modify my code to work in other ways, with Hooks for each transition or something like.

But straight to the answer.

// STATE MACHINE
function machine() {
    const self = this;

    let ea = undefined;
    let estadoatual = undefined;
    let estados = [];

    //Adiciona um estado se não houver um com mesmo nome
    function addstate(obj) {
        let found_state = false;
        for (var i in estados) {
            if (estados[i].name == obj.name) {
                found_state = true;
                //console.warn(`[STATE_MACHINE] You already have ${obj.name} state stored`);
                return
            }
        }
        if (found_state == false) {
            estados.push(obj);
            //console.log(`[STATE_MACHINE]> ${obj.name} <added to states!`);
        }
    }


    this.addstate = (obj) => {
        addstate(obj);
    };
    
    //Troca o estado e chama as funçoes onChange quando mudar de estado e onOpen quando entrar em um estado.
    function changeState(string) {
        for (var i in estados) {
            if (string == estados[i].name) {
                //console.log(`[STATE_MACHINE] Found: ${estados[i].name}`);
                if (string !== estadoatual) {
                    if (ea !== undefined) {
                        ea.onChange();
                    }
                    //console.log('[STATE_MACHINE] State changed to: ' + estados[i].name + '.');
                    estadoatual = estados[i].name;
                    ea = new estados[i].constructor();
                    ea.onOpen()
                }
            }
        }
    }

    this.changestate = (string, param) => {
        changeState(string, param);
    }

}

Basically we have two methods, "addstate" and "changestate", when calling addstate, you have to pass an object containing the 'name' method and one containing a constructor ('constructor') function, this function will have two methods, onOpen and onChange, which will be called respectively when a state is opened or when it is exchanged. Follow an example of code initialization.

const sm = new machine();

//Note que o objeto leva somente dois parametros, na função construtora, deve-se passar os parametros onOpen e onChange...

//Adiciona um estado:
sm.addstate({name:'FOO', constructor: function(){
  this.onOpen = () => {
    //Codigo para ser executado quando aberto.
  }
  this.onChange = () => {
    //Codigo para ser executado quando for trocado por outro estado.
  }
}});

Now that we have created a state machine and added one, just call Sm.changestate('Foo').

// Faz a troca de estados.
sm.changestate('FOO');

With this I got the answer I wanted, the code works asynchronously, not needing an order to be activated just call the changestate() method. Some of the other questions remain unanswered and I would very much like someone to help me with them...

Browser other questions tagged

You are not signed in. Login or sign up in order to post.