Basic Usage

Redux-ORM comes with excellent documentation. The main Redux-ORM README, Redux-ORM Primer tutorial, and the API documentation cover the basics very well, but here’s a quick recap.

Defining Model Classes

First, you need to determine your different data types, and how they relate to each other. Think of this in the same way you would set up a database schema, with tables, foreign keys, and so on. Then, declare ES6 classes that extend from Redux-ORM’s Model class. Like other file types in a Redux app, there’s no specific requirement for where these declarations should live, but you might want to put them into a models.js file, or a /models folder in your project

As part of those declarations, add a static fields section to the class itself that uses Redux-ORM’s relational operators to define what relations this class has. This can be done with three different forms of JS syntax: attaching the declaration to the class variable after it’s been declared; using a static get function on the class; or using the Class Properties syntax to declare a static field. You should also declare a "modelName" property so that table names are correctly generated even if the code is minified.

In Redux-ORM 0.9, the API was updated to allow you to declare what per-instance fields are expected for each model type in addition to the relational fields. It’s not required, but it allows the library to more efficiently generate properties that look up the right values for those fields from the store.

Here’s some basic examples of how to declare Redux-ORM Model classes. I’ll demonstrate each of the three approaches for adding the different relations and fields to the classes, but all three of them should behave the same way.

import {Model, attr, fk, oneToOne, many} from "redux-orm";

// Basic JS class syntax, with field declarations added to the class later
export class Pilot extends Model{
  // Other class methods would go here
}

Pilot.modelName = "Pilot";

Pilot.fields = {
  // An instance field on the model
  id : attr(),
  name : attr(),
  // A foreign key relation
  mech : fk("Battlemech"),
  // A one-to-one relation
  lance : oneToOne("Lance")
};



// Using static getter functions
export class Battlemech extends Model{
  static get modelName() {
    return "Battlemech";
  }
  
  static get fields() {
    return {
      id : attr(),
      name : attr(),
      pilot : fk("Pilot"),
      lance : oneToOne("Lance"),
    }
  }
}


// Using the Stage 3 Class Properties syntax
export class Lance extends Model{
  static modelName = "Lance"
  
  static fields = {
    id : attr(),
    name : attr(),
    // A many-to-many relation
    mechs : many("Battlemech"),
    pilots : many("Pilot")
  }
}

Creating an ORM Instance

Once you’ve defined your models, you need to create an instance of the Redux-ORM ORM class, and pass the model classes to its register method. This ORM instance will be a singleton in your application:

import {ORM} from "redux-orm";
import {Pilot, Battlemech, Lance} from "./models";

const orm = new ORM();
orm.register(Pilot, Battlemech, Lance);
export default orm;

Setting Up the Store and Reducers

Next, you need to decide how to integrate Redux-ORM into your reducer structure. Per the docs, there’s two ways to define reducer logic with Redux-ORM.

The primary approach is to write normal reducer functions that have access to the “database state” portion of your Redux store. In those reducers, you import the ORM instance, and use it to create a Session instance based on the initial DB state. After executing update commands via the Session, you would specifically return the updated DB state object generated by the Session instance. Here’s what that might look like:

// entitiesReducer.js
import orm from "models/schema";

// This gives us a set of "tables" for our data, with the right structure
const initialState = orm.getEmptyState();

export default function entitiesReducer(state = initialState, action) {
    switch(action.type) {
        case "PILOT_CREATE": {
            const session = orm.session(state);
            const {Pilot} = session;
            
            // Creates a Pilot class instance, and updates session.state immutably
            const pilot = Pilot.create(action.payload.pilotDetails);
            
            // Returns the updated "database tables" object
            return session.state;           
        }    
        // Other actual action cases would go here
        default : return state;
    }
}

// rootReducer.js
import {combineReducers} from "redux";
import entitiesReducer from "./entitiesReducer";

const rootReducer = combineReducers({
    entities: entitiesReducer
});

export default rootReducer;

The second approach is to define a reducer() function on your Model classes, then use the createReducer() utility method provided by Redux-ORM to create a reducer function you can add to your Redux store. The Model reducer methods will be called with the current action, a version of the current Model class that’s tied to the Session, and the Session itself:

// Pilot.js
class Pilot extends Model {
    static reducer(action, Pilot, session) {
        case "PILOT_CREATE": {
            Pilot.create(action.payload.pilotDetails);
            // Don't need to return anything. The Session was updated automatically, and 
            // its current state will be returned by the generated parent reducer
            break;
        }
    }
}

// rootReducer.js
import {combineReducers} from "redux";
import {createReducer} from "redux-orm";
import orm from "models/schema";

const rootReducer = combineReducers({
    entities : createReducer(orm)
});
export default rootReducer;

I personally prefer the first approach of “using Redux-ORM in my own reducers”, and actually have done that since I started using Redux-ORM, even though earlier versions of the docs suggested the “model-attached reducers” approach.

Selecting Data

Finally, the ORM instance can be used to look up data and relationships in selectors and mapState functions:

import React, {Component} from "react";
import orm from "./schema";
import {selectEntities} from "./selectors";

export function mapState(state, ownProps) {
    // Create a Redux-ORM Session instance based on the "tables" in our entities slice
    const entities = selectEntities(state);
    const session = orm.session(entities);
    const {Pilot} = session;
    
    const pilotModel = Pilot.withId(ownProps.pilotId);
    
    // Retrieve a reference to the real underlying object in the store
    const pilot = pilotModel.ref;    
    
    // Dereference a relation and get the real object for it as well
    const battlemech = pilotModel.mech.ref;
    
    // Dereference another relation and read a field from that model
    const lanceName = pilotModel.lance.name;

    return {pilot, battlemech, lanceName};
}

export class PilotAndMechDetails extends Component { ....... }

export default connect(mapState)(PilotAndMechDetails);

That covers the basic concepts. Next, we’ll look at Redux-ORM’s APi and concepts in more detail.

Create a free account to access the full course.

By signing up, you agree to Educative's Terms of Service and Privacy Policy