WRidder / react-spa

Community site SPA based on ReactJS
http://react-spa.herokuapp.com/
MIT License
297 stars 34 forks source link

Possibility of using omniscientjs? #1

Open dashed opened 9 years ago

dashed commented 9 years ago

I see the usage of Immutable-js; I'm wondering if you ever looked into using omniscientjs?

WRidder commented 9 years ago

Good suggestion. No, I was not aware of omniscientjs yet, looking into it now.

WRidder commented 9 years ago

I noticed here that you are already using omniscientjs with reflux. How did you implement these two together?

dashed commented 9 years ago

Here is an un-tested example; but essentially the same idea I've been using (keep in mind the implicit require()). This idea was based on this gist.

// cursor.js
var structure = immstruct({});

// store/name.js
var Actions = Reflux.createActions([
    'load',
    'update'
]);

var nameStore = Reflux.createStore({
    init: function() {
        this.listenTo(Actions.load, this.load);
        this.listenTo(Actions.update, this.update);
    },

    load: function() {
        structure.cursor().update(function(map) {

            return map
                .set('greeting', 'Welcome')
                .set('name', 'bob');

        });
    },

    update: function(newName) {

        structure.cursor('name').update(function() {
            return newName;
        });

        // No need to do this: this.trigger(...);
    }

});

// components/name.jsx

var mixin = {
    componentWillMount: function() {
        Actions.load();
    }  
};

var Name = component(mixin, function ({cursor}) {

    var onChange = function(e) {
        Actions.update(e.currentTarget.value);
    };

    return (
        <div>
            <h1>{cursor.get('greeting')} {cursor.get('name')}!</h1>
            <input value={cursor.get('name')} onChange={onChange} />
        </div>
    );
});

// app.js

function render () {
    React.render(
        Name(structure.cursor()),
        document.body
    );
}

render();

// structure.on('swap', render);
// or
structure.on('next-animation-frame', render);

Dataflow is the following:

Initial app state  +----->  Cursor +---> UI Component

                              ^               +
                              |               |
                              |               |
                              +               v

                            Stores <-----+  Actions
dashed commented 9 years ago

Alright. So, this is pretty much an anti-pattern with regards to isomorphism:

var mixin = {
    componentWillMount: function() {
        Actions.load();
    }  
};

Here are some ideas I've been throwing around.

https://gitter.im/omniscientjs/omniscient/archives/2014/12/05

I'm sketching out a thought experiment with hypothetical API. A component would usually require() actions/stores (implemented with some flux library) they need. When a store is loaded, init logic usually execute. Here, at each store's init, we can 'register' a function to a hypothetical CursorManager:

CursorManager.on('load', function(structure) {
    // async/sync stuff...

    return promise;
});

This function would be given a structure, from which you can 'extend' the cursor based on data changes at other areas in the cursor: structure.once('add', func). This is like 'async' code, so we create and return a promise.

We execute all functions registered to the load hook, and feed each of them with context-specific structure: CursorManager.execute('load', structure | structure.key);

Then when all the promises have resolved, the cursor would be built up by the data stores, then we can run React.render at the end step:

CursorManager.done('load', render);

This becomes flexible for isomorphic apps, where you instantiate an empty structure, fill with minimal state based on router path and cookies, then let data stores populate the cursor. Afterwards, you can render the UI component.

CursorManager behaves like an EventEmitter, but it isn't one.

WRidder commented 9 years ago

Thanks for your snippet, already gave me some new insights! I'll have to give it some further thought, quite important design decisions to be made here. Regarding omniscient on itself; activity seems to be low at the moment, will have to be sure it's going to be maintained on the long run.

Keep your ideas coming!

dashed commented 9 years ago

No problem! Will keep you posted on my findings. I'll keep an eye on this repo to see what you've come up with.

With regards to omniscient activity, I'd say it's definitely not abandoned. But pretty much on par with reflux's small footprint in its infancy.