Package Exports
- react-hydrate
- react-hydrate/dist/server
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (react-hydrate) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
react-hydrate
Generic data fetching and SSR hydration pattern for React.
Features & Goals
- Co-locate data dependencies with your components
- Supports infinitely nested loaders
- Fetches requested data on the server and hydrates on the client for a fast startup
- Wraps components so users can easily define loading states for components
- Routing agnostic. Works with
react-routerv4. - Lightweight ~1.9kb
Related: react-hydrate-link - prefetch data for your next route using react-router v4.
Usage
Defining components
/**
* Projects.js
*/
import api from 'my-api'
import { hydrate } from 'react-hydrate'
import Project from './Project.js'
export default hydrate(
/**
* dataLoader receives component props
* and any state already in the store
*/
(props, state) => {
return api.fetchProjects().then(projects => {
return {
projects: projects
}
})
},
/**
* mapStateToProps receives the
* loaded data via `state` and any
* component props.
*
* You should return `false` here if
* the data needed is not yet availabe.
* If a falsy value is returned, it
* tells the library that the loader
* hasn't been run yet or hasn't
* yet resolved.
*/
(state, props) => {
return state.projects ? {
projects: state.projects
} : false
}
)(({ loading, data, ...inheritedProps }) => {
/**
* Component is always passed a loading
* prop that represents the status of their
* dataLoader function
*/
return loading ? (
<div>Loading data...</div>
) : (
data.projects.map(project => <Project {...project} key={project.slug}>)
)
})/**
* App.js
*/
import React from 'react'
import Projects from './Projects.js'
export default props => (
<div>
<Projects />
</div>
)Creating root app
import React from 'react'
import { render } from 'react-dom'
import { BrowserRouter as Router } from 'react-router-dom'
import { Tap } from 'react-hydrate'
import App from './App'
render((
<Router>
<Tap hydrate={window.__hydrate || null}>
<App />
</Tap>
</Router>
), document.getElementById('root'))Server
import React from 'react'
import { renderToString } from 'react-dom/server'
import { StaticRouter: Router } from 'react-router'
import { Tap, createStore } from 'react-hydrate'
import { asyncRender } from 'react-hydrate/dist/server'
import App from './App.js'
app.use((req, res) => {
const ctx = {}
const store = createStore({})
const Root = (
<Router location={req.url} context={ctx}>
<Tap hydrate={store}>
<App />
</Tap>
</Router>
)
asyncRender(Root).then(() => {
const state = store.getState()
const content = renderToString(Root)
if (ctx.url) {
res.writeHead(302, {
Location: ctx.url
})
res.end()
} else {
res.send(`
<!DOCTYPE html>
<html>
<head></head>
<body>
${content}
<script>
window.__hydrate = ${JSON.stringify(state)}
</script>
<script src="/index.js"></script>
</body>
</html>
`)
res.end()
store.clearState()
}
})
})Dependencies
- react-tree-walker: Walk a React element tree. by @ctrlplusb
MIT License