Mobx: Quick way to converting Class-based components to use React Hooks

I’ve been using Mobx for a while now, and now that React hooks is officially out in React 16.8, I thought I give it a go and see how easy it is to convert class components to functional components.

Note: I haven’t benchmarked this approach, this is just an experiment.

import { useState, useEffect } from 'react';
import * as ReactDOM from 'react-dom';
import * as React from 'react';
import { observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
function useObservable(cb,deps = []) {
const [val, setState] = useState(cb);
useEffect(() => {
setState(cb());
return reaction(cb, v => setState(v));
},deps);
return val;
}
class Model {
@observable.ref text = <span>Some text</span>;
}
// Replace this class component
@observer
class ClassComponent extends React.Component{
render(){
return <div className="box">{model.text}</div>
}
}
// with this functional component 
function FunctionalComponent(props){
const { model } = props;
  return useObservable(()=><div className="box">{model.text}</div>);
}
const model = new Model(); 
ReactDOM.render(<FunctionalComponent model={model} />,
document.getElementById('test'));
var frame = 0; 
function tick(){
frame++;
model.text = <span>{frame}</span>
if (frame < 10000){
requestAnimationFrame(tick);
}
}
requestAnimationFrame(tick);

Things to note:

  • This is experimental. It hasn’t been benchmarked, but will be looking at properly benchmarking and ideally profile memory allocation and performance.
  • The custom hook useObservable above isn’t reliant on mobx-react and hence, you only need mobx to make it work.
  • The React hook above is a simplistic approach that assumes the observable object is created outside the component, which is a personal preference.
  • Feedback is most welcome.

Update: updated the code to make sure function passed to useEffect only gets called once.

Update: returned disposer to clean up the reaction observer

Update: as per Michel’s comment, updated to use useState(cb), frankly, didn’t know that useState accepts a function, thanks Michel Weststrate. That would indeed improve performance.