Managing State

The React Storefront framework uses mobx-state-tree for state management. It combines the ease of use of MobX's mutable, observable models, while providing structure to the app's state (similar to Redux). It supports the Redux Dev Tools Chrome Extension

Base Models

React Storefront provides an number of base model classes that implement common behavior and automatically integrate with analytics. These are found in react-storefront/model.

Accessing State

The root of the state tree in React Storefront is named app. You can access it from any component using mobx-react's @inject decorator. To make your component automatically rerender when injected state changes, add the @observer decorator.

import React, { Component } from 'react'
import { observer, inject } from 'mobx-react'
import Typography from '@material-ui/core/Typography'

@inject(({ app }) => ({ product: app.product })) // inject and observe the product branch of the state tree
@observer // `observer` should always be the last decorator!
export default class Product extends Component {

  render() {
    return (
      <Typography variant="h6">{product.name}</Typography>
    )
  }

}

Modifying State

To modify app state, call an action method on a model object. For example: to add a product to the cart, you can call the add method on react-storefront/model/CartModelBase:

import React, { Component } from 'react'
import { observer, inject } from 'mobx-react'
import Button from '@material-ui/core/Typography'

@inject(({ app }) => ({ product: app.product, cart: app.cart }))
@observer
export default class Product extends Component {

  render() {
    return (
      <Button onClick={this.addToCart}>Add to Cart</Button>
    )
  }

  addToCart = () => {
    this.props.cart.add(this.props.product)
  }

}

Only action methods can modify state. Action methods are defined on models like this:

import { types } from "mobx-state-tree"

const MyModel = types
  .model("MyModel", {
    value: types.number
  })
  .actions(self => ({
    increment() {
      self.value++
    },
    decrement() {
      self.value--
    }
  }))

const instance = MyModel.create({ value: 1 })
instance.increment() // value = 2
instance.decrement() // value = 1
instance.value = 2   // throws an error

Historical State

During client-side navigation, React Storefront saves snapshot of the application state in window.history.state. When the user navigates back or forward, React Storefront automatically restores the application's state from this snapshot. This makes backward and forward navigation very fast.

Typically, certain parts of the app state should not be restored from the snapshot when navigating back or forward. By default, React Storefront does not restore user and cart. This can be changed by overriding the retainStateOnHistoryPop action on AppModelBase.

Here is the default implementation:

// from react-storefront/AppModelBase

/**
 * Returns the part of the state tree which should not be overwritten
 * when the user goes forward or back.  You can override this action
 * to retain additional branches of the tree.
 */
retainStateOnHistoryPop() {
  return { 
    cart: self.cart.toJSON(), 
    user: self.user && self.user.toJSON() 
  }
}