github effector/effector effector@20.9.0
effector 20.9.0, effector-react 20.6.0

latest releases: effector@23.4.1, effector@23.4.0, effector@23.3.0...
5 years ago
  • Introduce effector/fork and effector-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 with restore 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

Try it

Don't miss a new effector release

NewReleases is sending notifications on new releases.