npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

diff-react

v1.0.5

Published

Smart React tree differ

Downloads

38

Readme

diff-react

The Smart™ React tree differ.

What's it do?

diff-react provides a single function for shallow-rendering and diffing two React components. The idea is that basic snapshotting, while great for small components that don't change frequently, doesn't scale well to large components or components that change frequently (since the snapshots tend to be brittle, breaking due to unrelated changes somewhere else in the component). Shallow rendering helps this somewhat but doesn't obviate the problem entirely. Instead, it would be better to create snapshots that only contain DOM changes relative to some baseline rendering.

For example, if you wanted to test that <MyButton disabled={true}/> properly grays out the text and icon inside, with Jest you could do something like:

expect(diffReact(
  <MyButton />,
  <MyButton disabled={true} />,
)).toMatchSnapshot();

This would then create a minimal diff snapshot that you could test against, highlighting only the relevant changes in your custom component and their context:

 <Button …=…
-   disabled={false}
+   disabled={true}
 >
   <Image …=…
     style={
       Object {
-        "tintColor": "#eee",
+        "tintColor": "#666",
       }
     }
   />
   <Text …=…
     style={
       Object {
-        "color": "#eee",
+        "color": "#666",
       }
     }
   >…</Text>
 </Button>

How's it work?

Well, from a high-level perspective:

  1. diffReact() receives two React elements and an optional canonicalization function.
  2. Using Jest's shallow renderer, it renders the elements one-level deep.
  3. virtualizeTree() then takes each shallowly-rendered tree and recursively converts each node into virtual-dom VNode, VText or SENTINEL_NULL_VNODE. As we traverse each node, we also canonicalize the props to ignore certain irrelevant changes later (e.g. non-rendering on* handler props).
  4. virtual-dom's diff() then performs a diff on the two canonicalized, VNode-based trees, returning basically a POJO that maps numeric in-order traversal node indices to patches (e.g. insert/remove/replace/change props/reorder operations).
  5. serializePatchesRecursively() recurses over the original in-order building a serialized diff as it goes based on the original VNode tree and the patch map:
    • For VTEXT and VNODE patches (which represent node replacements), serializeTree(), a simplified version of serializePatchesRecursively) that takes a VNode tree (without patches), an indent level, and a modification prefix (e.g. +/-) and returns a the tree rendered with those params, is used to render the before node (with -) and the after node (with +).
    • For PROPS patches (which represent props changes), iterate over the list of combined props from the original VNode and the patch, and serialize each one:
      • For props that are equal, combine them all into a single …=… prop.
      • For primitives that differ, print the before prop with a - prefix before each line and the after prop with a + prefix.
      • For arrays, objects, and React trees (all of which can be complex), serialize the values with with prettyFormat() and run a text diff on them. In order to provide shorter diffs than just entire before prop and the entire after prop, formatArrayOrObjectPropDiff() collapses equal chunks of text to .
    • For INSERT patches, serialize the patch.patch with serializeTree() and add it to the end of the children array with + prefixes. If the node was actually inserted in the middle of a list, we should expect an ORDER patch to follow, which will move it into the right location.
    • For REMOVE patches, serialize the node in-place using serializeTree() and - prefixes.
    • For ORDER patches (which represent children being reordered)... it's probably best explained by looking at the code directly.

This module also does some things like collapse all the children to if they're all equal and try to keep props on the same line as the start tag in serializeTree() if possible, but that covers the vast majority of this module's complexity.