react-collider
v1.10.5
Published
Express middleware for isomorphic express + react apps
Downloads
33
Maintainers
Readme
React-collider
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 availableurl
Url to calloptions
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 theforceFetch
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.