React without Pain: Avoid Re-Renders and Simplify Dependencies

vaukalak

vaukalak

Posted on May 13, 2024

React without Pain: Avoid Re-Renders and Simplify Dependencies

Tracking re-renderings in React can be a painstaking process. Fine-grain reactivity pattern offers a solution that completely removes this problem, allowing React engineers to forget about tracking dependencies and focus on writing code. In this article, we’ll explore how library Mlyn simplifies dependency management and prevents unnecessary re-renders in React applications.

Dependency Management: A Common Challenge

In traditional React applications, managing state and dependencies can often lead to complex and error-prone code. Let’s consider a simple counter example to illustrate this issue.

Traditional React Example

import React, { useState, useCallback } from "react";
const Counter = () => {
  const [count, setCount] = useState(0);
  const onIncrement = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, [count]); // Dependency on count
  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={onIncrement}>Increment</button>
    </div>
  );
};

export default Counter;
Enter fullscreen mode Exit fullscreen mode

Issue with Dependencies

In this example, the onIncrement function is wrapped in useCallback with count as a dependency. Every time count changes, onIncrement is re-created.

Simplifying with Mlyn

Mlyn introduces fine-grain reactivity, allowing you to manage state updates without worrying about dependencies. Let’s rewrite the same example using Mlyn.

import React, { useCallback } from "react";
import { rc, useSubject } from "mlyn/react";
const Counter = rc(() => {
  const state = useSubject({ count: 0 });
  const increment = useCallback(() => {
    state.count(state.count() + 1);
  }, []);
  return (
    <div>
      <h1>Count: {state.count()}</h1>
      <button onClick={onIncrement}>Increment</button>
    </div>
  );
});


export default Counter;
Enter fullscreen mode Exit fullscreen mode

Since the reference of state.count never changes, we can read it in useCallback without re-creating a new function.

Benefits of Fine-Grain Reactivity

  • Stable Callbacks: The onIncrement function in Mlyn does not need state.count as a dependency. This stability prevents unnecessary re-creation of the function. And consumers of onIncrement will not re-render when count changes.
  • Simplified Code: With Mlyn, you don’t need to track dependencies manually.

Extracting IncrementButton: A Closer Look at Re-Rendering

Let’s extract the increment button into the IncrementButton component to understand how Mlyn’s approach prevents unnecessary re-renders.

Traditional React: Re-Rendering Issue

In the traditional example, IncrementButton re-renders every time count changes because increment is a new function reference each time.

const IncrementButton = React.memo(({ onIncrement }) => {
  console.log("IncrementButton re-rendered");
  return (
    <div>
      <button onClick={onIncrement}>Increment from Child</button>
    </div>
  );
});

const Counter = () => {
  const [count, setCount] = useState(0);
  const onIncrement = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, [count]); // Dependency on count
  return (
    <div>
      <h1>Count: {count}</h1>
      <IncrementButton onIncrement={onIncrement} />
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Mlyn: Preventing Unnecessary Re-Renders

With Mlyn, onIncrement remains the same reference, so IncrementButton does not re-render unnecessarily. This is a key advantage of fine-grain reactivity.

Example Revisited

const Counter = rc(() => {
  const state = useSubject({ count: 0 });
  const onIncrement = useCallback(() => {
    state.count(state.count() + 1); // No dependencies needed
  }, []);
  return (
    <div>
      <h1>Count: {state.count()}</h1>
      <IncrementButton onIncrement={onIncrement} />
    </div>
  );
});
Enter fullscreen mode Exit fullscreen mode

Summary

Mlyn’s fine-grain reactivity simplifies state management by:

Eliminating the Need for Dependency Tracking: You can write cleaner, more maintainable code without worrying about manually updating dependencies.
Preventing Unnecessary Re-Renders: Components only re-render when their actual state or props change, improving performance and efficiency.
By adopting Mlyn, React engineers can focus on writing feature-rich code without the hassle of managing complex state dependencies. Try integrating Mlyn into your next project and experience the ease of fine-grain reactivity!

Interested in mlyn? check it out on github

💖 💪 🙅 🚩
vaukalak
vaukalak

Posted on May 13, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related