2

I'd like to have one prop in a component be an array of strings that is deep compared: <ChildComponent deepCompareThis={['a', 'b']} /> so I'd like to remove it from shallow comparison, as this array will always fail shallow comparison. I would like to keep this syntax for readability. I am using function components, so using React.memo.

I am curious if there is a clean way to use React.memo and deep compare one prop, while falling back to React's own default https://github.com/facebook/react/blob/master/packages/shared/shallowEqual.js for everything else.

Edit: looking at shallowCompare, it seems as long as you use Object.is for your other props values you should have the same comparison function. Perhaps it's not as scary as I thought to copy the comparison function. Most of it is optimization!

Yuji 'Tomita' Tomita
  • 115,817
  • 29
  • 282
  • 245
  • I believe there's nothing wrong with deep comparing one prop and shallowing the rest, however - most interesting part for me is `"this array will always fail shallow comparison`" - why does it happen? Do you create a new instance of that array with every render? Why? – kind user Sep 22 '20 at 20:47
  • @kinduser yeah, I create a new instance every render, to keep the code concise and in one location. Kind of the same logic why sometimes it's clean to write a tiny anonymous function, though I avoid because it will also fail a comparison. – Yuji 'Tomita' Tomita Sep 22 '20 at 20:50
  • Sounds like an anti pattern, but I believe you know what you are doing. – kind user Sep 22 '20 at 20:52
  • @kinduser I do not know what I am doing. :) – Yuji 'Tomita' Tomita Sep 22 '20 at 20:56
  • Then wouldn't be it better to fix the issue with re-creating the array instance with every render? I would gladly help you out with that if only you'd provide some code – kind user Sep 22 '20 at 20:57
  • That I'm not interested in, at the moment but thank you! I would love to explore passing native arrays into component props, as they can be chained down to children and manipulated easily, written/read easily for humans, etc. I can come up with various ways to prevent recreating the array... but the curiosity here is to pass the array! – Yuji 'Tomita' Tomita Sep 22 '20 at 21:05
  • But memoizing the array to avoid the re-render does not affect the array. You can still manipulate over them as a prop in children. I can see you are highly experienced with Python, however things are little bit different here, in React. – kind user Sep 22 '20 at 21:08
  • 1
    I do understand that.. I think the point you are missing, is that I am considering this scenario a learning exercise since I couldn't find an elegant solution to this. At this point, the most interesting thing isn't "what is the right answer for my particular application", it's "how can you do it?" Can you appreciate that? – Yuji 'Tomita' Tomita Sep 22 '20 at 21:11

1 Answers1

2

Here is my solution for this: (1) for the prop you are trying to deep compare you can write separate logic for that and (2) for the rest of the props, use the same logic that React has already written for shallow comparison. Have a look at my source code below & the comments in them for insight

const CustomComponent = React.memo(
  (props) => {
    React.useEffect(() => {
      console.log("CustomComponent render");
    });

    return "";
  },
  (prevProps, nextProps) => {
    // destructure the prop you want to specifically target for deep comparison
    // the rest will be shallow compared
    const { deepCompareThis: prevDeep, ...prevRest } = prevProps;
    const { deepCompareThis: nextDeep, ...nextRest } = nextProps;

    // your custom logic for the deep comparison
    // i took this function from https://stackoverflow.com/a/16436975/8680805
    function arraysEqual(a, b) {
      if (a === b) return true;
      if (a == null || b == null) return false;
      if (a.length !== b.length) return false;

      for (var i = 0; i < a.length; ++i) {
        if (a[i] !== b[i]) return false;
      }
      return true;
    }

    const deep_comparison_result = arraysEqual(prevDeep, nextDeep);
    console.log(`deep comparison`, deep_comparison_result);

    // use same logic provided by reeact for shallow comparison
    // from https://github.com/facebook/react/blob/master/packages/shared/shallowEqual.js
    const shallow_comparison_result = shallowEqual(prevRest, nextRest);
    console.log(`shallow comparison`, shallow_comparison_result);

    return deep_comparison_result && shallow_comparison_result;
  }
);

Edit React Memo - Only Deep Compare Selected Props

95faf8e76605e973
  • 13,643
  • 3
  • 24
  • 51
  • I like it! Thanks for taking the time to go through this. I found some good news on the shallow compare function! You can import it directly from the React dependencies, and I found other libraries using this same pattern. I have something similar if I can mutate the incoming props as they are read only. I was curious the cost of destructuring or spreading all the props to send to the shallow compare as well. Almost made a test.. then got distracted hard. – Yuji 'Tomita' Tomita Sep 23 '20 at 14:24