fluxxx

動手實作一個 Isomorphic flux 

flux 實作

  1. Dispatcher
  2. Store
  3. Action

Action

一種溝通工具,讓 view 跟 store 約定好要說什麼

避免雞同鴨講 


Fluxxx.action({
  go: function() {
    var flux = this.flux;
    flux.getActions().go.dispatch({abc: 123});
  }
});


//view 
this.flux().getActions().go()

Store

統一資料來源

Single Source of Truth

Fluxxx.store('list', function() {
  var flux = this.flux;
  var actions = flux.getActions();
  var list = [1, 2, 3];
  return {
    getList: function() {
      return list;
    },
    onPop: function() {
      list.pop();
      this.emit('change');
    },
    dehydrate: function() {
      return [list];
    },
    rehydrate: function(state) {
      list = state[0];
    },
    dispatchToken: flux.listenTo('list', function(on) {
      on(actions.pop, 'onPop');
    }
  }
});

Event System

div

input

span

listeners

 click、keydown、mosuedown、hover ...

Dispatcher

div

input

span

listeners

 click、keydown、mosuedown、hover ...

dispatcher

Problem

  • How to render after all stores are populated?
  • Singleton issue
  • Checksum mismatch

How to render after all stores are populated?

ajax action

How to render after all stores are populated?

var actions = Fluxx.action({
all: function() {
	var flux = this.flux;
	if(flux.getStore('list').hasInitialized())
		return;
	return fetch('/api/list.json').then(function(res) {
		return res.json();
	}).then(function(data) {
		flux.getActions().initial(data);
		return data;
	});
}})

ajax action

How to render after all stores are populated?

action

dispache

ajax action

How to render after all stores are populated?

action

dispache

ajax

solution

收集 promise

要 return action 的 promise

var actions = Fluxx.action({
all: function() {
	var flux = this.flux;
	return fetch('/api/list.json').then(function(res) {
		return res.json();
	});
}})


app.get('/', function() {
 ...
  flux.ready(function() {
    var html = React.renderToString(app);
    res.render('index', {html: html});
  });
...
});

Singleton Issue

Server 端禁止共用 dispatcher

每個 request 都要產生 dispatcher

store 跟 action 都要找得到對的 dispatcher

Before


var Dispatcher = require('flux').Dispatcher;
module.exports = new Dispatcher();

//action
var dispatcher = require('dispatcher');
var action = {
  all: function() {
    dispatcher.dispatch({type: 'all'});
  }
};


//store
var dispatcher = require('dispatcher');
var store = assign({}, EventEmitter.prototype, {
  onAll: function() {

  }
});

dispatcher.register(function(payload) {
 if(payload.type == 'all') {
   store.onAll(payload)
 }
})

After

//action
Fluxx.action({
  all: function() {
    var flux = this.flux;
  }
});
//store
Fluxx.store('list', function() {
  var flux = this.flux;
})

app.get('/', function() {
  var flux = new Fluxxx();
});

checksum 前端 store 不一致

  1. dehydrated
  2. rehydrated

在 store 定義存檔跟還原

Fluxxx.store('list', function() {
  var flux = this.flux;
  var actions = flux.getActions();
  var list = [1, 2, 3];
  return {
    dehydrate: function() {
      return [list];
    },
    rehydrate: function(state) {
      list = state[0];
    }
  }
});
var data = <?=json_encode($data);?>

後端的資料吐到前端塞回 store 裡面

//server
app.get('/', function() {
  var flux = new Fluxxx();
  React.renderToString(app);
  flux.ready(function() {
    var html = React.renderToString(app);
    res.render('index', {html: html, dehydratedStr: JSON.stringify(flux.dehydrate())});
  });
});


//browser

<script>var __dehydrated = <%- dehydratedStr %></script>

var flux = new Fluxx();
flux.rehydrate(window.__dehydrated);
React.render(<App flux={flux} />, document.getElementById('container'));

參考

總結

用手思考…

具體而微的瞭解 flux

Made with Slides.com