How to rewrite legacy project to ReactJS

Олег Кусов11.09.2022

We may want to rewrite legact app, for example BackboneJS or even ReactJS with custom state manager to something modern. In the first case it will be necessary to determine the rewriting strategy. If the application has complex pages then it is unlikely that it will be possible to rewrite it entirely at once so you have to figure out how to insert pieces of React components into the old stack and synchronize the state.

If the application has mostly simple pages, then you can use the trunk-based approach, in which small pieces of the page are rewritten and PR is thrown. At the same time the page is still invisible to users, but already lies in the main branch. Using this approach, you can greatly simplify the integration of new code, there will be practically no merge conflicts, and it will be easier for your colleagues review your code.

We can integrate ReactJS with BackboneJS like that:

function Paragraph(props) {
  return 

{props.text}

; } const ParagraphView = Backbone.View.extend({ initialize(options) { this.reactRoot = ReactDOM.createRoot(this.el); }, render() { const text = this.model.get('text'); this.reactRoot.render(); return this; }, remove() { this.reactRoot.unmount(); Backbone.View.prototype.remove.call(this); } }); const model = new Backbone.Model({ text: 'React works with Backbone!' }); const view = new ParagraphView({ model, el: "#app" }); view.render();

It possible to create reactMixin _.extend(ParagraphView.prototype, reactMixin) which will create reactRoot during initialization. To subscribe to changes in the BackboneJS model inside React, you can create a wrapper component that will accept the model object in props and take the necessary data from the Backbone model and also subscribe to changes if necessary.

const ReactHelloComponentAdapter = () => {
const [state, setState] = useState();  
function useForceUpdate(){
    const [value, setValue] = useState(0); 
    return () => setValue(value => value + 1);
}

const forceUpdate = useForceUpdate();
useEffect() {
    props.model.on('change', this.handleChange);
     return () => props.model.off('change', this.handleChange);
, [props.model]
}

const handleChange = () => {
 forceUpdate();
 setState('hello');
}

return 

There is also a library Backbone.ReactBridge to integrate Backbone and React.

Redux and BackboneJS Integration

The advantage of using Redux is that we do not depend on React rendering and can change the store directly. You can take backbone-redux. Under the hood, "redusers" with CRUD operations are created. CRUD actions are created through bindActionCreators and called on collection.on(...) backbone events.

Of course we can manually create our custom function which will call store.dispatch on collection.on or model.on events. If we want to create binding from Redux store to Backbone model (if Backbone model depends on Redux store state), we can use Redux store.subscribe function.

function handleChange() {
  const state = store.getState();
  users.set(state.user); //setting new records in users store  to backbone "users" collection  
}

const unsubscribe = store.subscribe(handleChange)
unsubscribe()