Introducing React Axiom November 16, 2016 edits reflect API changes since the original publishing of this article. At some point in a React application, describing state changes for application data becomes difficult at individual component levels and calls for a clean abstraction between business logic and presentational components. Redux is certainly an option at this point, but suppose the are unfavorable? tradeoffs that Redux provides is a lightweight (~12kb) way to use models with the React component tree. A basic React Axiom model looks like the following: React Axiom class ListItemModel extends ReactAxiom.Model { static defaultState() {return {id: null,description: '',completed: false};} } stores the argument object in and automatically creates getter and setter functions: , , for the property, , , for the property, and , , for the property (note: this is different due to the completed property being a boolean). Defining a method of the same name on the class overwrites the getter or setter: Model this.state getId setId hasId id getDescription setDescription hasDescription description isCompleted setCompleted hasCompleted completed class ListItemModel extends ReactAxiom.Model { static defaultState() {return {id: null,description: '',completed: false};} getDescription() {return this.state.description.toLowerCase();} } When a React Axiom model is passed into a component, the component listens to state changes within the model and updates itself. The following is an example of a React component using a model passed as below: listItem class ListItemComponent extends React.Component { render() {const { listItem } = this.props;return (<li>{listItem.getDescription()}{listItem.isCompleted() ? null : this.renderButton()}</li>);} renderButton() {const { listItem } = this.props;return (<button onClick={() => listItem.setCompleted(true)}>complete</button>);} } Notice how the component calls on the model to update state. To put everything together: setCompleted listItem const listItem = new ListItemModel({id: '1',description: 'Teach mom how to use Slack'}); const ListItemSubscriber = ReactAxiom.subscribe(ListItemComponent); ReactDOM.render(<ListItemSubscriber listItem={listItem} />,document.getElementById('app')); The higher order function wraps the and returns a new component. The component will then subscribe to the model and update itself if state changes. In the specific above example, clicking on the complete button will cause the button to disappear. subscribe ListItemComponent ListItemSubscriber ListItemSubscriber listItem Getting Complex with References State changes in React Axiom models occur through mutations. As a result, this design allows references to other objects, arrays, and models to operate fairly well in state. The following example adds a field to and some additional logic to complete a list item. dependencies ListItemModel class ListItemComponent extends ReactAxiom.Model { static defaultState() {return {id: null,description: '',completed: false,dependencies: [],};} complete() {this.getDependencies().forEach(dependency => {dependency.complete();}); this.setCompleted(true); } } To recursively render list items, the component can render the component for each dependency: ListItemComponent ListItemSubscriber class ListItemComponent extends React.Component { render() {const { listItem } = this.props;return (<li>{listItem.getDescription()}{listItem.isCompleted() ? null : this.renderButton()}<ul>{this.renderDependencies()}</ul></li>);} renderButton() {const { listItem } = this.props;return (<button onClick={() => listItem.complete()}>complete</button>);} renderDependencies() {const { listItem } = this.props;return listItem.getDependencies().map(dependency => (<ListItemSubscriber listItem={dependency} />));} } const ListItemSubscriber = ReactAxiom.subscribe(ListItemComponent); Now, clicking on a complete button will mutate that model’s state value and all of its dependencies’ state value. In addition, any component that subscribed to any of the mutated list items will update and render the correct state. completed completed Use with Server Rendering One particular drawback with React Axiom is that the data store requires model names to be passed as part of the transferrable data. This is fine for server-to-client data transfer, however it is less flexible in terms of snapshotting and persisting state to a local storage should the model names change in any way. The following is an example of serializing and parsing models and model data with the React Axiom : Store const listItem1 = new ListItemModel({id: '1',description: 'Teach mom how to use Slack'}); const listItem2 = new ListItemModel({id: '2',description: 'Meditate',dependencies: [listItem1]}); ReactAxiom.Store.setModelRefs([ListItemModel]); const serverStore = new ReactAxiom.Store({title: 'Things to do',createdAt: Date.now(),listItems: [listItem1, listItem2]}); const json = serverStore.stringify(); // Transfer the data to the client// to hydrate the client store, and// reinitialize the application. ReactAxiom.Store.setModelRefs([ListItemModel]); const clientStore = new ReactAxiom.Store(); clientStore.parse(json); Notice how contains a reference to on the server store. By passing in classes to initialize the client store, it is able to rebuild this reference from the provided JSON string. In other words, on . listItem2 listItem1 listItem2.getDependences()[0] === listItem1 clientStore When to Use Models Like any design, there are tradeoffs. Redux, for example, requires plain objects to describe changes in the system and pure functions to describe the logic for handling those changes (what a lot of developers refer to as “boilerplate”) but gains a more flexible data store. React Axiom requires models to describe the changes in the system describe the logic for handling those changes, but gains the following advantages: and Organizing and stubbing business logic becomes easier. Interfaces between views, models, and data become more flexible. Writing semantic and transparent code becomes easier. For someone new to the React ecosystem, React Axiom might make a little bit more sense than Redux, but like any framework or design that is opinionated, only use it if solves a particular problem or fits the situation. What is Next? React Axiom is still in its infancy stage. I welcome any help to make this design paradigm a robust solution for large scale applications for the JavaScript community. Please feel free to get involved by checking out the or . 🙏🏽 GitHub repository contacting me on Twitter