Sandy Gifford
Sandy Gifford

Reputation: 8136

Immutable.js with React Pure Components

I'm having some trouble using React PureComponents with Immutable.js. Consider the following demo:

https://codepen.io/SandoCalrissian/pen/QaEmeX

In it 2 components are rendered. The first (NoramlPure) is a normal PureComponent while the second (ImmutablePure) is a normal Component with a custom shouldComponentUpdate that checks props for an equals method. Both take a single Immutable Map as a prop which is re-formed on every render cycle. Both also print a message to the console every time they re-render.

I then trigger two render cycles.

I'm expecting both to only render once. However, the console shows the following:

rendering
    rendering normal pure component
    rendering immutable pure component
rendering
    rendering normal pure component

My custom component works as expected, but the built in PureComponent renders both times (despite getting the same data).

Seeing as React links to Immutable.js in the documentation for PureComponent (and since they're both made by Facebook) I'd sort of expect the two to work together naturally, but as far as I can tell PureComponents don't ever call any Immutable.js equality checkers.

Is there any way to make PureComponents work with Immutable.js objects? Or am I stuck using my PureImmutable component as a base-class across my entire project?

Upvotes: 3

Views: 1505

Answers (2)

herman
herman

Reputation: 12305

Currently you can use this ImmutablePureComponent implementation instead of the standard PureComponent:

https://www.npmjs.com/package/react-immutable-pure-component

Upvotes: 0

Johnny Zabala
Johnny Zabala

Reputation: 2465

Hi Sandy the problem is that in each render you are recreating the map prop and React.PureComponent is basically checking props.map === nextProps.map. They are different so it re-render.

Check this one:

class NormalPure extends React.PureComponent<any, any> {
  public render() {
    console.log("\trendering normal pure component");
    return <div />;
  }
}

class ImmutablePure extends React.Component<any, any> {
  public shouldComponentUpdate(nextProps) {
    return !Object.keys(this.props).every((key) => {
      const val = this.props[key];
      const nextVal = nextProps[key];

      if (typeof val.equals === "function")  {
        return val.equals(nextVal);
      } else {
        return val === nextVal;
      }
    });
  }

  public render() {
    console.log("\trendering immutable pure component");
    return <div />;
  }
}

const wrapper = document.getElementById("wrapper");

const obj = {
  "a": 1,
  "b": 2,
  "c": 3,
};

const immutableMap = Immutable.fromJS(obj);

function render() {
  console.log("rendering");

  ReactDOM.render(
    <div>
      <NormalPure map={immutableMap} />
      <ImmutablePure map={Immutable.fromJS(obj)} />
    </div>,
    wrapper
  );
}

render();
render();

Is there any way to make PureComponents work with Immutable.js objects? No. PureComponent use a strict equality comparison (===). It dosen't know about Immutablejs

Or am I stuck using my PureImmutable component as a base-class across my entire project? Yes, if you want to check Immutablejs objects with the equals method.

Upvotes: 3

Related Questions