When you are writing applications, eventually you have to decide how to manage state. You can get far with React setState
and lift the state in the component hierarchy as you go. Eventually that might become cumbersome and you realize using a state manager might save time and effort. This is the reason why solutions like Redux, MobX, and Cerebral are popular in the community.
To provide another point of view, you will hear this time from Nir Yosef, the author of controllerim. It's a solution that builds on top of MobX and has been designed testability in mind.
My name is Nir, and I am a front-end developer at Wix.com, with over two years of experience in React and MobX, and now gaining some experience with React Native and Android.
Controllerim is a state management library. It gives you the ability to create logic controllers for you React components, and makes your components automatically reactive to any change in the controllers. All of this is done with almost zero boilerplate.
Controllerim uses MobX Observables behind the scenes, so all the optimizations of MobX in term of performance are also relevant for Controllerim.
Controllerim brings back the idea of the well know Controller, the C of MVC, and abandon the singleton Stores concept that Redux (using Flux terminology) gave birth to.
When I first came across React, I almost immediately came across Redux. Its seems like Redux was the only way to do React. Everyone was talking about it, so I decided to give it a try.
After reading some tutorials, I was quite amazed by its complexity. All the different terms (thunk, reducers, selectors, map dispatch to props, etc.) weren’t so clear to me, and it seems like a considerable amount of boilerplate. Something just felt wrong. It seems like a strange way to implement the good old MVC. I think the article by André Staltz says it all.
After some playing around with dummy project, trying to crack this Redux thing, I came across MobX and dumped Redux for good.
MobX was much clearer and straightforward.
I used MobX for over a year with my team, and it was pretty good, but some problems immediately came up:
mobx.toJs()
conversions all over the place.mobx.inject
and mobx.provide
but those didn't play well with our tests.So MobX wasn’t perfect after all. At this point, I again started to wonder what happens to the good old MVC, Why things are getting so much more complicated on the web? And then I decided to write down all the pain points of our current architecture:
toJS
thing. I want everything to be a plain JavaScript object.AppStore
will be the root.After writing it down, I found out that I don’t have a Store anymore. I have a Controller. The good old Controller. I knew I was on the right track. The API was just written itself down. I just needed to figure out the way to make it happen, and it wasn’t so hard. The final result was Controllerim.
If you wonder about the name, I tried to name it “Controllers” but it was already taken. I tried React-controllers, but it was also taken. In Hebrew, the ‘im’ suffix is the plural suffix for the word controller, so I just named it Controllerim. :)
Let's say we have App
component as the root of our web app, and that we have Child
component deeply nested in the app.
Every data that we will put on the AppController will be available to all other components in the app for as long as the app is alive, so let's create an AppController
and put some application data on it:
class AppController extends Controller {
constructor(componentInstance) {
super(componentInstance);
this.state = { userName: "Bob" };
}
getUserName() {
return this.state.userName;
}
setUserName(name) {
this.state.userName = name;
}
}
So a controller is just an ES2015 class that extends Controller
and has some state and getters and setters methods.
Now let's connect the controller to the App
component:
class App extends React.Component {
componentWillMount() {
this.controller = new AppController(this);
}
render() {
return (
<div>
<h1>Welcome {this.controller.getUserName()}!</h1>
<compA />
<compB />
</div>
);
}
}
export default observer(app);
Easy right? We just need to init the controller in componentWillMount
, and we need to make sure that we wrap the component with observer
, and that's it! Every change in the controller will be reflected by the view.
Now, let's say that Child
is some deeply nested component and that it should allow us to preview and edit the userName
when we click on a save button:
Let's start with creating ChildController
:
class ChildController extends Controller {
constructor(componentInstance) {
super(componentInstance);
this.state = { input: "" };
}
getInput() {
return this.state.input;
}
setInput(value) {
this.state.input = value;
}
saveInput() {
this.getParentController("AppController").setUserName(
this.state.input
);
}
}
The only new thing here is the call to getParentController()
. Controllerim allows you to get any Parent
controller, not only a direct parent, so we just save the userName
, and because everything is reactive, this change will be reflected in all the views that make use of userName
prop from App
. Let's finish by creating Child
:
class Child extends React.Component {
componentWillMount() {
this.controller = new ChildController(this);
}
render() {
return (
<div>
<input
value={this.controller.getInput()}
onChange={(e) =>
this.controller.setInput(e.target.value)
}
/>
<button onClick={() => this.controller.saveInput()}>
Save
</button>
</div>
);
}
}
And that's it! Simple isn't it?
It depends. The ones that are already familiar with MobX are very supportive. The Redux people are more suspicious and begin to recycle arguments they heard about MobX, so I think it would be nice to tackle down the two most frequently recycled arguments once and for all:
this.setState({some: {nested:{prop: true }}})
, you can just write this.state.some.nested.prop = true
.Use Controllerim all over the place to make it battle tested. :)
I think that Controllerim has the potential to be the best Redux alternative out there. In general, I think that React is here to stay, and the next giant step will be in the field of CSS.
If something doesn't feel right, don’t be fooled by its popularity.
You should interview someone from the CSS community. This field in the web development needs a little push.
Thanks for the interview Nir! Controllerim looks like a great abstraction over MobX and I hope people find it. The code feels almost amazingly simple.