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

@incoqnito.io/smart-component

v1.3.0

Published

A HOC enabling you to easily implement extended property equality checks for your React Components

Downloads

10

Readme

smart-component

A HOC enabling you to easily implement extended property equality checks for your React Components.

Motivation

The Problem

When writing components whose performance is crucial (for example top level components or components you render A LOT [something among the lines of > 700 times]) you will often want them to only update, when their visual appearance will change.

How do you do that? Right! Extending from React.PureComponent - but wait! My component is still rerendering, even though the data it receives doesn't change?!

This can often be caused by passing objects to your component - be it a JS Date(), an array of entities to render or even the styles property. Why?

React.PureComponent only does a swallow equality check. This means it does a comparison using the === operator on every key/value pair of the old properties and compares it to the value in the new properties. However === on objects (arrays are also objects in this manner) will only check wether or not they reference to the same chunk of memory - not if their content is the same.

Don't believe me? Just try { key: true } === { key: true } in your console. It'll actually return false! And that's exactly what happens, if you use a PureComponent like that:

<MyPureComponent style={{ width: '100%' }} />

It rerenders every time its parent rerenders (or more often), because on every parent render a new object is beeing created (with the same content or key/value pairs but pointing at a different chunk of memory, so oldProps.style === newProps.style will return false) - resulting in your PureComponent thinking it received changed props every time its parent rerenders.

The Cure

Implementing your own (shouldComponentUpdate(nextProps))[https://reactjs.org/docs/react-component.html#shouldcomponentupdate] every time!

But this can be quiet time consuming, as you will have to handle every single prop which might change and cause the need to rerender - who likes writing countless lines of if (this.props.key !== nextProps.key) { return true } (well in addition to the props where you need to do deep equality checks or something equal)? Of course you can automate that (reducing over the entries of your props), but than you are in the need of ugly switch (...) { case ... } for every prop you want to treat differently...

That's why smart-component is there for you!

Just give it equality checks for the props you want to treat special (in a very clean and verbose way) and let it handle the rest using good old ===!

Installation

npm install --save @icoqnito.io/smart-component

Usage

index.js

import React from 'react'
import {
  DateDisplay,
  SmartDateDisplay
}

export default class extends React.PureComponent {

  state = {
    date: new Date()
  }

  // this function generates a copy of the current value of this.state.date and sets the state to this copy
  // -> that means that the new value of this.state.date describes the same point in time as the old one
  // however (because new Date() instantiates a new JS Date() object) date === new Date(date) will return false
  // causing a PureComponent to rerender as it only checks using the === operator
  resetDate = () => {
    this.setState({
      date: new Date(this.state.date)
    })
  }

  onStandardRender = () => {
    this.setState((prevState) => ({ standardRenderedCount: prevState.standardRenderedCount + 1 }))
  }

  onSmartRender = () => {
    this.setState((prevState) => ({ smartRenderedCount: prevState.smartRenderedCount + 1 }))
  }

  render () {
    return (
      <div>
        <span>Standard Component rendered {this.state.standardRenderedCount} times<span>
        <DateDisplay date={this.state.date} onRender={this.onStandardRender} />

        <div> &nbsp </div>

        <span>Smart Component rendered {this.state.smartRenderedCount} times<span>
        <SmartDateDisplay date={this.state.date} onRender={this.onSmartRender} />

        <div> &nbsp </div>

        <button type='button' onClick={this.resetDate}>date = new Date(date)</button>
      </div>
    )
  }

}

DateDisplay.js

import React from 'react'
import smartComponent from 'smart-component'

export class DateDisplay extends React.PureComponent {

  render () {
    this.props.onRender && this.props.onRender()

    return (
      <div>{ props.date }</div>
    )
  }

}

export const SmartDateDisplay = smartComponent({
  date: (a, b) => a.getTime() === b.getTime()   // comparison function to compare the values of props.date at two different points in time; true -> equal
  // getTime() returns the timestamp as a number, making you able to compare them for equality just like normal numbers
}, DateDisplay)

You will see the standard component calling its onRender every time you click the button - opposing to the Smart Component, which uses its custom equality check implemented by you to prevent useless rerenders.