React + Flux and Server-side rendering? (Isomorphic React + Flux)
Asked Answered
B

4

43

What is the general practice of setting the initial state of the app with isomorphic applications? Without Flux I would simple use something like:

var props = { }; // initial state
var html = React.renderToString(MyComponent(props);

Then render that markup via express-handlebars and display via {{{reactMarkup}}.

On the client-side to set the initial state I would do something like this:

if (typeof window !== 'undefined') {
    var props = JSON.parse(document.getElementById('props').innerHTML);
    React.render(MyComponent(props), document.getElementById('reactMarkup'));
}

So yes essentially you are setting the state twice, on server and client, however React will compare the differences and in most cases so it won't impact the performance by re-rendering.


How would this principle work when you have actions and stores in the Flux architecture? Inside my component I could do:

getInitialState: function() {
  return AppStore.getAppState();
}

But now how do I set the initial state in the AppStore from the server? If I use React.renderToString with no passed properties it will call AppStore.getAppState() which won't have anything in it because I still don't understand how would I set the state in my store on the server?

Update Feb. 5, 2015

I am still looking for a clean solution that does not involve using third-party Flux implementations like Fluxible, Fluxxor, Reflux.

Update Aug. 19, 2016

Use Redux.

Burkitt answered 6/12, 2014 at 21:41 Comment(3)
hi, did you find the solution without third-party flux implementations?Analytic
Can't you just add AppStore.setAppState({...}) method for use on server? With Node.js being singlethreaded and renderToString synchronous method it should work without problems.Quartana
Just add a static getStore to the component and a load method to the Store and load data before rendering that way. You may also want to add a createStore to the store so you call it before loading new data in every request.Universalist
A
1

If you are willing to work with alt.js you can achieve it with alt.bootstrap and alt.flush (docs)

I'm using node js with react server side rendering and alt.js as my flux implementation.

This is how it looks:

var data = {}; // Get the data whatever you want and return it bootstrap ready.

// Reminder - renderToString is synchronised
var app = React.renderToString(
     AppFactory(data)
);

// In this point the react rendering was finished so we can flush the data and reset the stores

alt.flush();

In my app.jsx

/**
 *
 */
componentWillMount: function () {

    // This beauty here is that componentWillMount is run on the server and the client so this is all we need to do. No need for other third-party isomorphic frameworks

    alt.bootstrap(
        JSON.stringify(this.props, null, 3)
    );

}
Antimacassar answered 29/8, 2015 at 11:47 Comment(0)
C
14

Take a look at dispatchr and yahoo's related libraries.

Most flux implementations don't work in node.js because they use singleton stored, dispatchers, and actions, and have no concept of "we're done" which is required to know when to render to html and respond to the request.

Yahoo's libraries like fetchr and routr get around this limitation of node by using a very pure form of dependency injection (no parsing functions for argument names or anything like that).

Instead you define api functions like this in services/todo.js:

create: function (req, resource, params, body, config, callback) {

And actions like this in actions/createTodo.js:

module.exports = function (context, payload, done) {
    var todoStore = context.getStore(TodoStore);
...
context.dispatch('CREATE_TODO_START', newTodo);
...
context.service.create('todo', newTodo, {}, function (err, todo) {

The last line indirectly calls the create function in services/todo.js. In this case indirectly can either mean:

  • on the server:
    • fetchr fills in the extra arguments when you're on the server
    • it then calls your callback
  • on the client side:
    • the fetchr client makes a http request
    • fetchr on the server intercepts it
    • it calls the service function with the correct arguments
    • it sends the response back to the client fetchr
    • the client side fetchr handles calling your callback

This is just the tip of the iceberg. This is a very sophisticated group of modules that work together to solve a tough problem and provide a useable api. Isomorphism is inherently complicated in real world use cases. This is why many flux implementations don't support server side rendering.

You may also want to look into not using flux. It doesn't make sense for all applications, and often just gets in the way. Most often you only need it for a few parts of the application if any. There are no silver bullets in programming!

Chelton answered 6/12, 2014 at 22:21 Comment(2)
Thank you for an excellent answer. I have looked at Yahoo's todo example but did not like the fact it introduces new things like fluxible and fetchr. But as you said Flux on the server is inherently complex so that may be the only viable solution at the moment. You might be right, since I am not building a full blown single page application - just a small part of it in React, maybe Flux does not fit in here.Burkitt
Any time you can know what data the component needs ahead of time, the server side code is very simple. You fetch the data, pass it to the component as props, and put it in a <script> tag as json so it can be loaded on the client. This is usually feasible when you're just using it for a small part of the site.Chelton
M
3

FakeRainBrigand is correct that the biggest problem with server-side Flux is singletons. Flummox fixes this problem by not using singletons, and enabling you to encapsulate your entire Flux set-up into a single, reusable class. Then you just create a new instance on each request. Combined with a routing solution like React Router, you can make fully isomorphic applications.

Even if you don't want to use Flummox, the source is easy to grok and you could use that as a guide to whip something up yourself:

https://github.com/acdlite/flummox

Mazurek answered 5/2, 2015 at 21:0 Comment(0)
A
1

If you are willing to work with alt.js you can achieve it with alt.bootstrap and alt.flush (docs)

I'm using node js with react server side rendering and alt.js as my flux implementation.

This is how it looks:

var data = {}; // Get the data whatever you want and return it bootstrap ready.

// Reminder - renderToString is synchronised
var app = React.renderToString(
     AppFactory(data)
);

// In this point the react rendering was finished so we can flush the data and reset the stores

alt.flush();

In my app.jsx

/**
 *
 */
componentWillMount: function () {

    // This beauty here is that componentWillMount is run on the server and the client so this is all we need to do. No need for other third-party isomorphic frameworks

    alt.bootstrap(
        JSON.stringify(this.props, null, 3)
    );

}
Antimacassar answered 29/8, 2015 at 11:47 Comment(0)
P
0

Problem is that when you search for "Flux server rendering", you immediately bump to this question and there's no mentioning of Redux, made by React.js community rackt. You can find nicely described on Redux's documentation why server rendering is important, why we need to send initial state within the HTML to client (which is where Flux becomes insufficient) and how to do so.

Popovich answered 17/11, 2015 at 23:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.