Reduce React re-renders with mlyn.

Mikhail Boutylin
Frontend Weekly
Published in
3 min readSep 4, 2021

--

The problem: you have an application, where a component updates state, that should be displayed in an absolutely different part of the app. The simplest solution would be to “prop-drill” the value through your components hierarchy.

Suppose the following app:

It has only 4 components:

  • App: application container (declares the state)
  • A: Slider
  • B: div where the value of the slider is rendered
  • C: container of B

Possible implementation in plain react:

import React, { useState } from "react";const A = React.memo(({ progress, setProgress }) => {
return (
<input
type="range"
value={progress}
onChange={(e) => {setProgress(e.target.value);}}
/>
);
});
const B = React.memo(({ progress }) => {
console.log(">>> render B");
return <div>{progress}</div>;
});
const C = React.memo(({ progress }) => {
console.log(">>> render C:", progress);
return (
<div style={{ padding: 10, backgroundColor: "yellow" }}>
<B progress={progress} />
</div>
);
});
export const App = React.memo(() => {
const [progress, setProgress] = useState(0);
console.log(">>> render App");
return (
<div>
<A progress={progress} setProgress={setProgress} />
<C progress={progress} setProgress={setProgress} />
</div>
);
});

(You can run a full example on this sandbox)

If we want to update display value of the slide in B, this is what will happen on every update

Mlyn solves this problem by introducing proxy values called subjects. You can pass those subjects (or parts of them) to your children and subscribe / update only where needed. Let review the migration of the code above by steps:

export const App = () => {
const progress$ = useSubject(0);
console.log(">>> render App");
return (
<div>
<A progress$={progress$} />
<C progress$={progress$} />
</div>
);
};

Here in the App, we create a subject with an initial value of 0 . You can do following with a subject: subscribe to it value, write a new value to it, and pass it as a reference to another component.

const A = ({ progress$ }) => {
const progress = useSubjectValue(progress$);
return (
<input
type="range"
value={progress}
onChange={(e) => {
progress$(e.target.value);
}}
/>
);
};

To bind a subject value to a react component you should useSubjectValue function from react-mlyn package. And to update it, simply invoke it as a function passing a new value.

The value of progress$ subject can be passed through other components of your app. However, only the component in which you’ve subscribed to the subject will be re-rendered.

const B = ({ progress$ }) => {
console.log(">>> render B");
const progress = useSubjectValue(progress$);
return <div>{progress}</div>;
};
const C = ({ progress$ }) => {
console.log(">>> render C");
return (
<div style={{ padding: 10, backgroundColor: "yellow" }}>
<B progress$={progress$} />
</div>
);
};

This is how updating B diagram simplifies:

--

--