node package manager
Easy sharing. Manage teams and permissions with one click. Create a free org »

react-collider

React-collider Build Status

Express middleware for isomorphic Express + React apps. Also usable for any NodeJs app without Express.

Check out the example folder for a working example, including data-fetching from the Dailymotion API.

Features

  • Handle server and client side rendering
  • First call is done server side, subsequent calls use only api
  • Takes care of data fetching when needed
  • Possibility to serve your app by a cdn
  • Data fetching is done at the component level
  • Seo ready

Installation

$ npm install --save react-collider

Usage

Server side

Simply add the server middleware in your express app, giving your routes as argument.

import express  from 'express'
import collider from 'react-collider/server'
import routes   from './routing'
 
var app  = express(),
    port = process.env.PORT || 3000
 
app.use(collider(routes))
 
app.listen(port, () => {
    console.log('Listening on 127.0.0.1:' + port)
})

Logging

You can have informations in a log file:

// logs to react-collider.log 
app.use(collider(routes, {log: true}))
 
// logs to a custom file path 
app.use(collider(routes, {log: path.join(__dirname, 'server.log')}))

Client side

Similar: call the client module with your routes.

import collider from 'react-collider/client'
import routes   from './routing'
 
collider(routes)

Components

If your component must fetch some data before being rendered, use a fetchData static method. It must return an object or an array of objects with expose, url and params keys. Expose is the name under which the data will be available.

The fetchData method will receive an argument being the params from the router.

Example of a simple component:

export default class Home extends React.Component {
    static fetchData(params) {
        return {
            expose: 'home-data',
            url: `https://api.dailymotion.com/video/${params.videoId}`
        }
    }
    render() {
        var videos = getVideoList(this.props.data['home-data'])
 
        return (
            <div>
                <h1>Homepage</h1>
                {videos}
            </div>
        )
    }
}

When your component includes another component which needs data too, define a getDependencies static method to return an array of components:

import Sidebar from './sidebar'
import Footer  from './footer'
 
export default class Home extends React.Component {
    static getDependencies() {
        return [Sidebar, Footer]
    }
    render() {
        return (
            <div>
                <Sidebar data={this.props.data.Sidebar} />
                <div>
                    <h1>Homepage</h1>
                </div>
                <Footer data={this.props.data.Footer} />
            </div>
        )
    }
}

Data Provider

The dataProvider module allows data fetching from a url or from the initial data fetched server-side.

dataProvider(component, url, options)

  • expose String. The name under which the data will be available
  • url Url to call
  • options Object. Available options:
    • once: Removes the data from the local variable after use. This means the next time you call the same data it will fetch them remotely. Default to true.
    • forceFetch: Fetches the data remotely even if the data are available locally. Default to false.
    • set: Sets the data locally after fetching them remotely. The next time you need them they will be taken locally (unless you use the forceFetch option). Default to false.
import provider from 'react-collider/dataProvider'
 
class Video extends React.Component {
    componentDidMount() {
        var data = provider('video', `https://api.dailymotion.com/video/${params.id}?fields=id,title`, {once: true})
    }
}

Client side app only

If your servers are down and you can't pre-render the pages server-side, your app will still work client side (assuming your API is okay). All you need is to send a basic html file with your app bundled. Check out the example folder for an example.

Usage without Express

You can use react-collider wihtout express. You can simply use it to get the React component to render and the data to use:

import collider from 'react-collider'
import routes   from './routing'
 
var url = '/video'
 
// simply provide your routes and the url 
collider(routes, url, null, (Handler, data) => {
    var page = React.renderToString(React.createElement(Handler, {data: data}))
})

Custom data fetching

By default the module runs every fetchData methods of the components. If you need to handle yourself the data fetching you can pass a custom module that will receive an array of components needing to fetch data. It must return a promise.

You can use a custom fetch handler for server as well as client side. You can obviously choose to use a custom fetch handler server side but not client side (or the opposite), or a different one.

var routes   = require('./routing'),
    customFetchHandler = require('./fetch-handler')
 
// server side 
app.use(collider(routes), {fetch: customFetchHandler})
 
// or client side 
collider(routes, {fetch: customFetchHandler})
// Custom fetch handler 
var Promise = require('bluebird')
 
module.exports = function fetchHandler(components, params) {
    return new Promise(function(resolve) {
        var dataSet = {}
 
        components.forEach(function(component) {
            // handle the data fetching the way you want 
            // component.fetchData(params) 
        })
 
        resolve(dataSet)
    })
}

You will be able to handle the components the way you want. Check out the default fetch handler to see an example.