Frontend Development 13 min read

Performance Optimization in React: Diff Computation, Fiber Architecture, and Custom Renderers

The article explains how React’s virtual‑DOM diff can become a performance bottleneck, reviews classic optimizations such as debouncing, PureComponent, and idle callbacks, then details how the Fiber architecture makes diffing interruptible and prioritized and how the same reconciler can be leveraged to build custom cross‑platform renderers.

NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
Performance Optimization in React: Diff Computation, Fiber Architecture, and Custom Renderers

React uses a Virtual DOM and a diff algorithm to update the real DOM. While this simplifies development, the diff computation is not free and can become a performance bottleneck when many elements are involved, leading to dropped frames and sluggish UI.

The main reasons are:

Older React versions (<15) use a Stack Reconciler that performs recursive diffing, which cannot be interrupted.

The browser runs JavaScript and rendering on separate threads that are mutually exclusive; heavy JavaScript execution blocks UI rendering.

Common front‑end performance optimizations include:

Debounce

Delay state updates until user input is finished, preventing frequent re‑renders.

class App extends Component {
  onChange = () => {
    if (this.timeout) { clearTimeout(this.timeout); }
    this.timeout = setTimeout(() => {
      this.setState({ ds: [] });
    }, 200);
  };
  render() {
    return (
);
  }
}

PureComponent / shouldComponentUpdate

Use shallow comparison of props and state to skip unnecessary diff calculations.

class App extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    return (
      !shallowEqual(nextProps, this.props) ||
      !shallowEqual(nextState, this.state)
    );
  }
  render() { /* same as above */ }
}

Key considerations when using this approach:

a. Only shallow comparison is feasible; deep comparison may be slower than the diff itself.

b. Preserve object reference integrity when updating state; mutating nested objects can prevent updates.

class App extends PureComponent {
  state = { record: {} };
  componentDidMount() {
    const { record } = this.state;
    record.name = "demo";
    this.setState({ record });
  }
  render() { return <>{this.state.record.name}; }
}

c. Functions that capture external variables must be aware of changes to those variables.

class App extends PureComponent {
  cellRender = (value, index, record) => {
    return record.name + this.name;
  };
  render() { return
; }
}

Object Hijacking (MobX / Vue style)

Observe objects directly and avoid calling setState, enabling fine‑grained updates.

@inject("color")
@observer
class Btn extends React.Component {
  render() {
    return (
{this.props.text}
);
  }
}

Only the button re‑renders when its color changes.

requestIdleCallback

The browser API allows scripts to run during idle periods, which can be used to defer low‑priority work such as diff calculations.

requestIdleCallback(deadline => {
  if (deadline.timeRemaining() > 0) {
    // perform work
  } else {
    requestIdleCallback(otherTasks);
  }
});

Using idle time raises challenges: the diff must be interruptible and resumable, and tasks need priority tagging. React Fiber was introduced to address these concerns.

Fiber‑based Fibonacci Example

Fiber transforms recursive algorithms into an explicit loop with a stack‑like structure, preserving intermediate state for interruption.

function fib(n) {
  let fiber = { arg: n, returnAddr: null, a: 0 };
  rec: while (true) {
    if (fiber.arg <= 2) {
      let sum = 1;
      while (fiber.returnAddr) {
        fiber = fiber.returnAddr;
        if (fiber.a === 0) {
          fiber.a = sum;
          fiber = { arg: fiber.arg - 2, returnAddr: fiber, a: 0 };
          continue rec;
        }
        sum += fiber.a;
      }
      return sum;
    } else {
      fiber = { arg: fiber.arg - 1, returnAddr: fiber, a: 0 };
    }
  }
}

React Fiber adopts a similar approach, expanding the call stack into a linked list of Fiber nodes.

Typical Fiber node fields include tag, type, key, child, sibling, return, pendingProps, memoizedProps, pendingWorkPriority, stateNode, effectTag, etc.

{
  tag,
  type,
  key,
  child,
  sibling,
  return,
  pendingProps,
  memoizedProps,
  pendingWorkPriority,
  stateNode,
  effectTag,
  ...
}

Custom Renderer

React Reconciler can be used to build a custom renderer by implementing a HostConfig. The core method createInstance maps a component type to a platform‑specific instance (e.g., document.createElement for the web).

import Reconciler from 'react-reconciler';
const HostConfig = { /* ... */ };
const CustomRenderer = Reconciler(HostConfig);
let root;
function render(children, container) {
  if (!root) {
    root = CustomRenderer.createContainer(container);
  }
  CustomRenderer.updateContainer(children, root);
}
render(
, document.querySelector('#root'));

Cross‑platform renderers can intercept createInstance to return native components, such as a MobileButton for mobile targets.

import { MobileButton } from 'xxx';
createInstance(type, props) {
  const components = { Button: MobileButton };
  return new components[type](props);
}

API design trade‑offs include special handling for text nodes (shouldSetTextContent, createTextInstance) that are necessary for the DOM but often unnecessary for other hosts.

export function shouldSetTextContent(type, props) {
  return (
    type === 'textarea' ||
    type === 'option' ||
    type === 'noscript' ||
    typeof props.children === 'string' ||
    typeof props.children === 'number' ||
    (typeof props.dangerouslySetInnerHTML === 'object' &&
     props.dangerouslySetInnerHTML !== null &&
     props.dangerouslySetInnerHTML.__html != null)
  );
}

In non‑DOM environments the function can simply return false.

export function shouldSetTextContent() { return false; }

Conclusion

The article explores why React introduced Fiber, how its architecture enables interruptible and prioritized rendering, and how the Custom Renderer pattern leverages the same reconciler for diverse platforms. It also provides code snippets and references for further study.

front‑endPerformance OptimizationReactFiberCustom Renderer
NetEase Cloud Music Tech Team
Written by

NetEase Cloud Music Tech Team

Official account of NetEase Cloud Music Tech Team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.