Looking for a React.memo() Example

Does anyone have an example of React.memo() - Trying to prevent a component from re-rendering caused by high up contextual updates.

This component rendered a PayPal Checkout button for instance, so lots of re-renders makes PayPal pretty upset it seems. Dealing with flashing and random loading failures caused by scripts being loading multiple times, dom elements being removed, etc (this is my guess) - So the solution seems to at least try to prevent this from re-rendering so much.

Thanks

I do use some in this project:

It is in reason syntax, but should not be too different from rescript :slightly_smiling_face:

Hope it helps!

1 Like
[@react.component]
let make = () => {
  let (state, dispatch, actions) = Reducers.make();

  <div className=Styles.gameWrapper>
    <div className=Styles.game>
      <Layers.Base state={state.general} actions />
      <Layers.Decorations state={state.decorations} />
      <Layers.Path state={state.path} />
      <Layers.Objects state={state.objects} />
      <Layers.Mobs state={state.mobs} calculatedPath={state.path.calculatedPath} dispatch />
      <Ui state={state.ui} actions />
    </div>
  </div>;
};

let make = React.memo(make)

Interesting…but this would not work in this case.

If your component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result
(https://reactjs.org/docs/react-api.html#reactmemo)

In this case the component does not recieve any props, but updates close to every 16ms with an internal state change through the reducer(s).
Once this happens not all Layers need to update. They only have to update if specific data changes (listed in the dependencies of React.memo).

Also wrapping the Layer components themselves in a pure React.memo call would not work either, because they are recieving functions through the actions prop. Functions are, when you compare them to each other, never the same and woud cause the layers to update even if no data changed.

Is using React.useMemo the same as using React.memo?

Not quite, no.

With React.useMemo, you have to add a dependency array as the second argument (same as useEffect). useMemo will then only compute the function (given in the first argument) again, if one of the dependencies in the dependency array change.

React.memo is similar, but it is a higher order component, not a hook. That means, you can only add it around one of your components.

Example JS:

const MyComponent = React.memo(function MyComponent(props) {
  /* render using props */
});

It then behaves exactly like a PureComponent (if you are familiar with them). Basically it will only re-render, if one of its props changes.

Pretty sure you can wrap the make function with memo directly, rather than making a separate variable. Is there a reason you did it that way?

1 Like

Sure, it’s the exact same thing, I just find that way more readable
I dont make another variable, I “shadow” the first one

3 Likes

Did anyone ever find an answer to this?

Looks like wrapping the make function definition in React.memo is indeed sufficient. For example, in the following code, changing the Input’s text will only rerender Counter if the first letter is changed:

module Counter = {
  @react.component
  let make = React.memo((~prefix) => {
    let (count, setCount) = React.useState(() => 0)
    Console.log("Counter rendered")
    <div>
        <p>{String.concat(prefix, Int.toString(count))->React.string}</p>
        <button onClick={_ => setCount(c => c + 1)}>{"Increase"->React.string}</button>
    </div>
  })
}

module Input = {
  @react.component
  let make = () => {
    let (text, setText) = React.useState(() => "")
    Console.log("Input rendered")
    <div>
        <input onChange={c => setText(_ => JsxEventU.Form.target(c)["value"])} value={text}/>
        <Counter prefix={text->String.slice(~start=0, ~end=1)} />
    </div>
  }
}

1 Like

This doesn’t seem to work for me with React.memoCustomCompareProps

memoCustomCompareProps is a bit clunky due to how @react.component works, but it’s doable:

module Counter = {
  type propDef = {prefix: string}
  let customMemo = React.memoCustomCompareProps(_, (p1, p2) => p1.prefix == p2.prefix)
  @react.component(:propDef)
  let make = customMemo((~prefix) => {
    let (count, setCount) = React.useState(() => 0)
    Console.log("Counter rendered")
    <div>
        <p>{String.concat(prefix, Int.toString(count))->React.string}</p>
        <button onClick={_ => setCount(c => c + 1)}>{"Increase"->React.string}</button>
    </div>
  })
}

module Input = {
  @react.component
  let make = () => {
    let (text, setText) = React.useState(() => "")
    Console.log("Input rendered")
    <div>
        <input onChange={c => setText(_ => JsxEventU.Form.target(c)["value"])} value={text}/>
        <Counter prefix={text->String.slice(~start=0, ~end=1)} />
    </div>
  }
}

Thanks @cwstra, I really appreciate all your answers!

What does the colon mean in @react.component(: props)?

Not really sure. I just aped the syntax from the PR that added it.

(From tinkering, it might be for passing a type as an argument to a decorator, rather than a value)