Easier React Component State Management Using jQuery-style Getters and Setters

Nicholas Baroni
3 min readJan 9, 2017

--

Out with the old and in with the new. That’s largely the sentiment driving the React community because the paradigm changes brought about by React’s virtual-dom were so revolutionary that you really could rethink everything and sometimes even had to. But sometimes the old ways of doing things can really get you where you need to go. Today we’re going to be reaching back into jQuery and borrowing a technique that will help to simpify our React component state management.

I’ve been developing in React for a little over a year now. Overall, I’m very happy with it. One aspect of React that I have found a bit tedious is using the component state. It’s not bad within the component, but it becomes tedious when you have to pass down the state and handlers or setters to child components. For example, let’s suppose we have 2 keys in state. It doesn’t matter what they are.

import React from 'react';export default class MyContainer extends React.Component {
constructor(props, ...rest) {
super(props, ...rest);
this.state = { modalIsOpen: true, view: 'summary' };
}
setModalOpen(modalIsOpen) {
this.setState({ modalIsOpen });
}
setView(view) {
this.setState({ view });
}
render() {
return (
<MyComponent
{...this.props}
modalIsOpen={this.state.modalIsOpen}
viewMode={this.state.viewMode}
setModalOpen={this.setModalOpen.bind(this)}
setView={this.setView.bind(this)}
);
}
}

Some people advise binding your setters in the constructor. I chose to keep it simple for this example, and after our refactor, it won’t be an issue. You can see that we’re passing down two state values and two setters.

The inspiration to refactor this into something simpler came from jQuery and Mithril.js. In Mithril, there is the concept of a view model, which is essentially what state is in a React component. It’s the UI state, or the state that holds values concerned with displaying the model.

The first part of the refactor is simple. Instead of passing down each state value, we’ll pass down the state object and refer to it as a view model.

import React from 'react';export default class MyContainer extends React.Component {
constructor(props, ...rest) {
super(props, ...rest);
this.state = { modalIsOpen: true, view: 'summary' };
}
setModalOpen(modalIsOpen) {
this.setState({ modalIsOpen });
}
setView(view) {
this.setState({ view });
}
render() {
return (
<MyComponent
{...this.props}
viewModel={this.state}
setModalOpen={this.setModalOpen.bind(this)}
setView={this.setView.bind(this)}
);
}
}

That makes things just a tad more convenient, but the primary benefit is that it sets us up for the next refactor.

Mithril has a function m.prop that turns a property into a getter/setter method, similar to how jQuery works. For example, from the m.prop docs:

//define a getter-setter with initial value `John`
var name = m.prop("John");

//read the value
var a = name(); //a == "John"

//set the value to `Mary`
name("Mary"); //Mary

//read the value
var b = name(); //b == "Mary"

That is what we will be doing. We’ll convert our React state values into getter/setter functions so that we won’t have to make setter functions for each value in state.

I’ll wait to post the code for how to do this. For now, let’s look at what our container will look like after the refactor. We’ll also show the component, so we can see the getter/setters being used.

Container

import React from 'react';
import viewModel from '../../utils/view-model';
export default class MyContainer extends React.Component {
constructor(props, ...rest) {
super(props, ...rest);
this.state = viewModel(this, {
modalIsOpen: true,
view: 'summary'
});
}
render() {
return (
<MyComponent {...this.props} viewModel={this.state} />
);
}
}

Component

import React from 'react';
// import Modal and other components
export default function MyComponent(props) {
const view = props.viewModel.view();
return (
<div>
<ul>
<li onClick={() => props.viewModel.view('details')}>
Detail View
</li>
<li onClick={() => props.viewModel.view('summary')}>
Detail View
</li>
</ul>
{view === 'summary' && <SummaryView />}
{view === 'details' && <DetailView />}
<Modal
open={props.viewModal.modalIsOpen()}
onClose={() => props.viewModal.modalIsOpen(false)} />
</div>
);
}

The setter function also works well when you child component’s invokes the setter with a value.

<ChildComponent setCount={props.viewModel.count} />

View Model Utility

export function vmGetterSetter(componentInstance, value, key) {
return nextValue => {
if (typeof nextValue === 'undefined') return value;
componentInstance.setState({
[key]: vmGetterSetter(
componentInstance,
nextValue,
key
)
});
};
}
export default function viewModel(componentInstance, values) {
return Object.keys(values).reduce((state, key) => {
state[key] = vmGetterSetter(
componentInstance,
values[key],
key
);
return state;
}, {});
}

The other upside to this is that it helps to declutter props. Your entire view model along with all the setters is contained in one object.

--

--