- Introduce
effector/fork
andeffector-react/ssr
: api for server side rendering and managing independent instances of application in general.
/**
* app
*/
import {createDomain, forward, restore} from 'effector'
import {useStore, useList, Provider, useEvent} from 'effector-react/ssr'
export const app = createDomain()
const requestUsername = app.createEffect<{login: string}, string>()
const requestFriends = app.createEffect<string, string[]>()
const username = restore(requestUsername, 'guest')
const friends = restore(requestFriends, [])
forward({
from: requestUserName.done.map(({result: username}) => username),
to: requestFriends,
})
const Friends = () => (
<ul>
{useList(friends, friend => (
<li>{name}</li>
))}
</ul>
)
const Title = () => <header>Hello {useStore(username)}</header>
export const View = ({root}) => (
<Provider value={root}>
<Title />
<Friends />
</Provider>
)
/**
* client
*/
import ReactDOM from 'react-dom'
import {fork} from 'effector/fork'
import {app, View} from './app'
const clientScope = fork(app, {
values: window.__initialState__,
})
ReactDOM.hydrate(<View root={clientScope} />, document.getElementById('root'))
/**
* server
*/
import express from 'express'
import {renderToString} from 'react-dom/server'
import {fork, serialize, allSettled} from 'effector/fork'
import {app, View} from './app'
export const server = express()
server.get('/user/:login', async (req, res) => {
// clone application
const scope = fork(app)
// call requestUsername(req.params) in scope
// and await all triggered effects
await allSettled(requestUsername, {
scope,
params: req.params, // argument for requestUsername call
})
// save all stores in application to plain object
const data = serialize(scope)
// render dom content
const content = renderToString(<View root={scope} />)
res.send(`
<body>
${content}
<script>
window.__initialState__ = ${JSON.stringify(data)};
</script>
</body>
`)
})
This solution requires effector/babel-plugin
in babel configuration:
{
"plugins": ["effector/babel-plugin"]
}
Example application with express
Serverless example
- Add events created with
createApi
, stores created withrestore
and events created with.prepend
to domain of given source units
import {createDomain, createApi, restore} from 'effector'
const domain = createDomain()
domain.onCreateStore(store => {
console.log('store created')
})
domain.onCreateEvent(event => {
console.log('event created')
})
const position = domain.createStore({x: 0})
// => store created
const {move} = createApi(position, {
move: ({x}, payload) => ({x: x + payload}),
})
// => event created
const lastMove = restore(move, 0)
// => store created