elfi

React bindings

elfi ships with some React bindings so you can quickly start working on an application using both of them. They are available in the elfi/react module.

storeShape

The storeShape object allows you to specify that a component’s prop should be a store, which means it has the dispatch, getState and subscribe methods available.

import { storeShape } from "elfi/react"

function Counter({ store }) {
  return <p>{store.getState()}</p>
}

Counter.propTypes = {
  store: storeShape.isRequired,
}

ElfiContext and connect

elfi ships with an integration for the official React Context API that’s been available since React 16.3.0. To use it, you must wrap your app in the context provider ElfiContext.Provider and you can use ElfiContext.Consumer in your child components to consume store data.

You’ll also probably want to automatically trigger updates using the subscribe method from the store. Here is a typical app startup code using elfi/react:

// app.js
import React from "react"
import ReactDOM from "react-dom"
import { createStore } from "elfi"
import { ElfiContext } from "elfi/react"

import Counter from "./Counter"

const store = createStore(0) // Our state is a simple integer
const root = document.getElementById("app-root")

document.addEventListener("DOMContentLoaded", renderApp)
store.subscribe(renderApp)

function renderApp() {
  ReactDOM.render(
    <ElfiContext.Provider value={store}>
      <Counter />
    </ElfiContext.Provider>,
    root,
  )
}

In order to simplify consumption of store data in your components, elfi ships with a connect higher order component that does all the dirty work for you. It takes a component as a first argument and an function that maps store data to props as a second argument. This mapping function takes the store state and the store itself as arguments, and must return an object of props.

Using the previous app bootstrap code, here’s an example usage of the connect HOC to build a component:

// Counter.js
import React from "react"
import { connect } from "elfi/react"

function Counter({ value }) {
  return <p>Current value is {value}</p>
}

export default connect(
  Counter,
  value => ({ value }),
)

Dispatching actions from components

Dispatching actions from your React components is no different than using elfi outside of the React environment. To continue on our counter example, here’s how you would increment the counter’s value when clicking on it:

// Counter.js
import React from "react"
import PropTypes from "prop-types"
import { storeShape, connect } from "elfi/react"

// Our increment action
function increment(state) {
  return state + 1
}

class Counter extends React.Component {
  static propTypes = {
    store: storeShape.isRequired,
    value: PropTypes.number.isRequired,
  }

  handleClick = () => {
    const { store } = this.props
    store.dispatch(increment)
  }

  render() {
    const { value } = this.props
    return <p onClick={this.handleClick}>Current value is {value}</p>
  }
}

export default connect(
  Counter,
  value => ({ value }),
)

You can try this demo online

Fork me on GitHub