Simple replacement for PureComponent or React.memo()

reasonreact

#1

This is a cross-post from the Discord server – apologies for that, but it looks like my question might have been swallowed by the timeline!

Is there currently a simple way to replicate PureComponent behaviour in ReasonReact? The retainedProps API adds a lot of boilerplate and I really miss being able to just extend PureComponent or wrap a component in recompose/pure or React.memo()


#2

Ok I have come up with a potential solution using a functor, but I’d love a second opinion on my approach.

You use it like this. It will ensure that render will only be called again if retainedProps is not structurally equal to the previous retainedProps. You can see that the boilerplate is reduced compared to the docs example, with the main difference being that you don’t need to define your own shouldUpdate, just like in ReactJS, if you use PureComponent, you don’t need to define your own shouldComponentUpdate.

module Config = {
  type retainedProps = {
    view: string,
    subview: option(string),
  };
  let name = "Breadcrumb";
};

module Pure = PureComponent.Stateless(Config);

let make = (~view, ~subview=?, _children) => {
  ...Pure.component,
  retainedProps: Config.{view, subview},
  render: _ => <View> ... </View>,
};

And the implementation looks like this:

// PureComponent.re

module type StatelessConfig = {
  type retainedProps;
  let name: string;
};

module Stateless = (Config: StatelessConfig) => {
  let component = {
    ...ReasonReact.statelessComponentWithRetainedProps(Config.name),
    shouldUpdate:
      (
        {oldSelf, newSelf}:
          ReasonReact.oldNewSelf(
            ReasonReact.stateless,
            Config.retainedProps,
            ReasonReact.actionless,
          ),
      ) =>
      oldSelf.retainedProps != newSelf.retainedProps,
  };
};

If the general consensus on this is good, I’ll probably publish it on npm!


#3

I like this setup. Definitely less code. My initial thought was using some higher order component, or render component, and this kind of looks like that but typed! It would be great to see this up on NPM.

I wonder if it’s worth it to create some comparable to pass to the functor to compare the values. I am not sure how reason does object comparison.


#4

Yes this is an excellent idea (adding a comparable). Do you know if there’s a way to make this optional (i.e. providing the structural equality function as a fallback) without requiring the comparable to be wrapped in an option?


#5

Hmmm, I think you would pass the comparable in config as let comp = option(comparable). Then inside of the component you would do Belt.Option.getWithDefault(comp, equals). In this case equals would just be a simple function that takes both props and compares them using equality. If the user did not want to pass a comp then they could pass none. That’s all I can think of at the moment. I am not sure how to just provide a default if the user provides no comp at all.


#6

#7

Nice!