-
Notifications
You must be signed in to change notification settings - Fork 141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rerendering and async workflow #5
Comments
Damn it, I posted the comment without finishing it...will post the rest now |
This is my setup # orders/components/Orders.cjsx
React = require 'react'
{Getter} = require 'nuclear-js'
NuclearReactMixin = require 'nuclear-react-mixin'
reactor = require '../../Reactor'
OrdersStore = require '../OrdersStore'
reactor.attachStore 'ordersStore', OrdersStore, false #silent
module.exports = React.createClass
mixins: [NuclearReactMixin(reactor)]
componentDidMount: ->
OrdersStore.getState()
getDataBindings: ->
orders: Getter 'ordersStore.orders' # uses KeyPath
total: Getter 'ordersStore', (o) -> o.get('total')
render: ->
<div className='orders'>
<h1>There are {@state.total} orders</h1>
<ul>
{@state.orders.map (order) ->
<li>{order.get('id')}: {order.get('total')}</li>
.toJS()}
</ul>
</div> # orders/OrdersStore.coffee
{Immutable, Store} = require 'nuclear-js'
{Map, List} = Immutable
OrdersActions = require './OrdersActions'
module.exports = Store
getInitialState: ->
Map
orders: List()
total: 0
count: 0
offset: 0
initialize: ->
# subscribe to dispatches
@on 'ordersFetched', (state, payload) ->
state.mergeDeep
orders: payload.results
total: payload.total
count: payload.count
offset: payload.offset
getState: ->
# let's get some real data
OrdersActions.fetchOrders() # orders/OrdersActions.coffee
reactor = require '../Reactor'
WS = require '../shared/libs/WS'
module.exports =
fetchOrders: (opts = {}) ->
WS.get '/api/orders'
.then (result) ->
reactor.dispatch 'ordersFetched', result
.fail (e) ->
console.error e
# TODO: we can dispatch for an error # WS.coffee
$ = require 'jquery'
module.exports =
get: (url) ->
$.ajax
url: url
dataType: 'json' |
So here are my questions:
Any idea / help would be much appreciated :) |
Did you figure it out @emmenko? I am currently trying to implement async workflow as well. @jordangarcia do you have any best practices? |
Here are some of the patterns & best practices Optimizely's used in its Flux implementation:
An simplified example of this workflow: /**
* Dispatches action when an API fetch request starts
* @private
*
* @param {string} entity
* @param {Deferred} deferred
*/
function onFetchStart(entity, deferred) {
flux.dispatch(apiActionTypes.ENTITY_FETCH_START, {
entity: entity,
deferred: deferred,
})
}
/**
* Dispatch an ENTITY_FETCH_SUCCESS event to the system
* @private
*
* @param {string} entity
* @param {object|array} response
*/
function onFetchSuccess(entity, response) {
flux.dispatch(apiActionTypes.ENTITY_FETCH_SUCCESS, {
entity: entity,
data: response,
})
return response
}
/**
* Dispatch an ENTITY_FETCH_FAIL event to the system
* @private
*
* @param {string} entity
* @param {string} reason
*/
function onFetchFail(entity, reason) {
flux.dispatch(apiActionTypes.ENTITY_FETCH_FAIL, {
entity: requestInfo.get('entity'),
reason: reason,
})
return reason
}
function fetchAll(filters) {
var entity = 'projects'
var onSuccess = onFetchSuccess.bind(null, entity)
var onFail = onFetchFail.bind(null, entity)
var url = generateApiUrl(entity, filters)
var deferred = $.ajax(url).then(onSuccess, onFail)
onFetchStart.call(null, entity, deferred)
return deferred
} There is a store that listens for these api actionTypes and it looks like var Nuclear = require('../../nuclear')
var apiActionTypes = require('../constants/api-action-types')
var toImmutable = Nuclear.toImmutable
module.exports = Nuclear.Store({
getInitialState() {
return {}
},
initialize() {
this.on(apiActionTypes.ENTITY_FETCH_SUCCESS, loadEntityData)
this.on(apiActionTypes.ENTITY_PERSIST_SUCCESS, loadEntityData)
this.on(apiActionTypes.ENTITY_DELETE_SUCCESS, onDeleteSuccess)
}
})
/**
* payload.data
* payload.entity
* @param {Immutable.Map} state
* @param {object} payload
*/
function loadEntityData(state, payload) {
var entity = payload.entity
var data = payload.data
if (!_.isArray(data)) {
data = [data]
}
return state.withMutations(state => {
data.forEach(entry => {
state.setIn([entity, entry.id], toImmutable(entry))
})
})
}
/**
* payload.id
* payload.entity
* @param {Immutable.Map} state
* @param {object} payload
*/
function onDeleteSuccess(state, payload) {
var entity = payload.entity
var id = payload.id
return state.removeIn([entity, id])
} This store when populated looks like: // Immutable.Map
{
projects: {
1: { id: 1, description: 'project 1', ... },
2: { id: 1, description: 'project 2', ... },
3: { id: 1, description: 'project 3', ... },
},
users: {
1: { id: 1, username: 'user1', ... },
2: { id: 1, username: 'user2', ... },
},
} All data bindings in the UI are between components and the store (named React.createClass({
mixins: [NuclearReactMixin(reactor)],
getDataBindings() {
return {
projects: 'modelCache.projects',
}
},
}) By programming this reactively using Getters and The main drawback to this style is knowing when specific requests are done, we've solved this by creating a In VueJS it looks like this ui.loadingWhen('main-section', ajaxDeferred) <div v-loading="main-section">...</div> This Hope this helps! It's quite a different paradigm of programming asynchronous workflows, but having a reactive functional mindset and composing Getters together makes it very powerful and scales quite well. |
Wow thanks for the detailed answer @jordangarcia! |
@jordangarcia thanks for the explanation :) |
The question about using |
Hi @jordangarcia
I'm currently learning React and the Flux architecture and came to your library when researching how to use Stores with Immutables. I've stumbled upon a couple of problems / questions though when applying it to my example, so let me explain a bit what I did.
In my example I want to fetch some data from an API when the component is rendered. In the ideal scenario with a Flux architecture it would look like this:
Now, when I implement this flow using the
Reactor
the last step is not executed, which could be the same problem @ArnoBuschmann is having #4The text was updated successfully, but these errors were encountered: