Memo/useMemo/useCallback all the things!

Overview

eslint-plugin-react-usememo

Enforce that functions or complex objects that can generate unecessary renders or side-effects are wrapped in useMemo or useCallback, allow for devs to enforce that functional components be wrapped in memo programatically, and that all props and deps are wrapped in useMemo/useCallback; The intended outcome is that component's tree and/or expensive lifecycles (e.g. React Native's FlatLists, useEffect, useMemo, etc) only re-calculate or render again when really necessary, controlling expensive expressions and bringing out the best scalability and performance that your application can get.

Rationale

Why we memo all the things.
React Native's own docs state how it's important to use static or memoized as props for complex children (FlatList on that case), that applies even more broadly when we are talking about custom components (the Components you've created), it might not seem necessary at first but you'll be making a bet that the component in question will never grow to use memo or those props in hooks (i.e. useEffect, useMemo, useCallback), you'll only notice once your solution starts freezing and dropping frames, that's why using the require-usememo rule is recommended.

Installation

yarn add @arthurgeron/eslint-plugin-react-usememo --dev

or

npm install @arthurgeron/eslint-plugin-react-usememo --save-dev

Usage

To enable the plugin add the following to the plugin property your eslintrc file:

"plugins": ["@arthurgeron/react-usememo"],

Then enable any rules as you like, example:

"rules": {
    "@arthurgeron/react-usememo/require-usememo": [2],
},

Rules

require-usememo Recommended

Requires complex values (objects, arrays, functions, and JSX) that get passed props or referenced as a hook dependency to be wrapped in React.useMemo() or React.useCallback().

Options:

  • {strict: true}: Fails even in cases where it is difficult to determine if the value in question is a primitive (string or number) or a complex value (object, array, etc.).

Incorrect

function Component() {

  const [data, setData] = useState([]);
  
  
  // This will be redeclared each render
  function renderItem({ item }) {
    return (<Text>item.name</Text>);
  }

  // Data isn't redeclared each ender but `[]` is
  return (<FlatList renderItem={renderItem} data={data ?? []} />);
}

Incorrect (class component)

class Component() {

  constructor(props) {
    super(props);
    this.state = {
      data: undefined,
      propDrivenData: props.,
    };
  }
  
  
  // This will NOT be redeclared each render
  getItemName(item) {
    return item.name;
  }

  render() {
    // This function will be redeclared each render
    function renderItem({ item }) {
      return (<Text>{this.getItemName(item)}</Text>);
    }

    // Data isn't redeclared each ender but [] is
    // Extradata has a exponential complexity (will iterate the entire array for each render, could render once or several times in a second)
    // Outcome will be that any new render on this component will cause the entire FlatList to render again, including children components, even if the data hasn't changed.
    return (<FlatList
      renderItem={renderItem}
      data={data ?? []} 
      extraData={dataArray.filter(id => !!id)}
    />);
  }
}

In the previous example there are two issues, a function and a object that will be dynamically redeclared each time the component renders, which will cause FlatList to keep re-rendering even when the input data hasn't changed.

Correct

// Has no dynamic dependencies therefore should be static, will be declared only once.
function renderItem({ item }) {
  return <Text>item.name</Text>;
}

const EMPTY_ARRAY = [];

function Component() {

  const [data, setData] = useState(EMPTY_ARRAY);
  
  // Will only render again if data changes
  return (<FlatList renderItem={renderItem} data={data ?? EMPTY_ARRAY} />);
}

Correct (class component)

// Static therefore is only declared once
const EMPTY_ARRAY = [];

class Component() {
  constructor(props) {
    super(props);
    this.state = {
      data: undefined,
      propDrivenData: props.dataArray.filter(id => !!id),
    };
  }
  
  // Properly regenerate state driven data only when props change instead of during each render
  static getDerivedStateFromProps(props) {
    if (props.propDrivenData !== this.props.propDrivenData) {
      return {
        propDrivenData: props.dataArray.filter(id => !!id),
      };
    }
    return null;
  }

  // Will be declared only once.
  getItemName({item}) {
    const { data } = this.state;
    const dataLength = data ? data.length : 0;
    return (<Text>{item.name} {dataLength}</Text>);
  }

  render() {
    const { data } = this.state;
    // Will only cause a new render if data changes
    return (<FlatList renderItem={this.renderItem} data={data ?? EMPTY_ARRAY} />);
  }
}

Correct

const EMPTY_ARRAY = [];

function Component() {
  const [data, setData] = useState(EMPTY_ARRAY);
  const [isEditing, setIsEditing] = useState(false);

  // Has dynamic dependencies but will only be re-declared when isEditing or the input data changes
  const renderItem = useCallback(({ item }) => {
    return (<Text>{isEditing ? 'item.name' : 'Editing'}</Text>);
  }, [isEditing]);

  return (<FlatList renderItem={renderItem} data={data ?? EMPTY_ARRAY} />);
}

require-memo

Requires all function components to be wrapped in React.memo().
May be useful when used with overrides in your eslint config, I do not recommend enabling this globally, while there's great advantaje in memoing a complex tree of components some smaller/basic components with no children might not need to be memoized.

Incorrect

export default function Component() {
  return (<Text>This is a component</Text>);
}

Correct

export default memo(function Component() {
  return (<Text>This is a component</Text>);
});

require-usememo-children Advanced

Requires complex values (objects, arrays, functions, and JSX) that get passed as children to be wrapped in React.useMemo() or React.useCallback().

Options:

  • {strict: true}: Fails even in cases where it is difficult to determine if the value in question is a primitive (string or number) or a complex value (object, array, etc.).

Incorrect

function Component() {

  return (<View>
    <>
    <OtherComponent />
    </>
  </View>);
}

Correct

function Component() {
  const children = useMemo(() => (<OtherComponent />), []);
  
  return (<View>
    {children}
  </View>);
}
You might also like...
react-redux-toastr is a React toastr message implemented with Redux, primary consists of three things: a reducer, toastr emitter and a React component.
react-redux-toastr is a React toastr message implemented with Redux, primary consists of three things: a reducer, toastr emitter and a React component.

react-redux-toastr demo react-redux-toastr is a React toastr message implemented with Redux, primary consists of three things: a reducer, toastr emitt

Remember the things you already did

🥰 You already did! Remember the things you already did Deploy of the app 💡 What is it? Website that helps you remember that you're amazing and you'v

Hooks to build things like tooltips, dropdown menu's and popovers in React
Hooks to build things like tooltips, dropdown menu's and popovers in React

Hooks for positioning tooltips & popovers react-laag provides a couple of tools to position UI elements such as tooltips and popovers with ease. It le

Package containing a few microinteractions you can use to reward your users for little things and make them smile!
Package containing a few microinteractions you can use to reward your users for little things and make them smile!

Demo available here! Usage This package was built using React-Pose, react-dom-confetti and Lottie-web. Why should I use that? Read my blog post and yo

✍ It has never been so easy to document your things!
✍ It has never been so easy to document your things!

Docz makes it easy to write and publish beautiful interactive documentation for your code. Create MDX files showcasing your code and Docz turns them i

Use-tasker - A react task runner for asynchronous things

useTasker A React Hook to schedule asynchronous things. Install yarn add use-tas

Register-wifi - A tool to help get IoT style things online

Register Wifi A tool to help get IoT style things online. My use case is a inter

A light-weight, comprehensive, reactive framework for business logic and when things change.

watchlight.dev v1.0.15 beta For when things change. A light-weight, comprehensive, reactive framework for business logic and when things change. In

Web application that allows users to buy and sell various things of different categories
Web application that allows users to buy and sell various things of different categories

This simple and functional application allows users to sell and buy their products. It's open for anybody around the world, in this way the users can deal with all currencies as well as crypto currencies to close their business.

Find & watch all React conference talks, built for all React developers!
Find & watch all React conference talks, built for all React developers!

ReactConf.TV is a place where passionate React developers are able to search & watch organized and up-to-date react conference videos

A complete template all you need for your next react projects, with all the Best Practices of JAMStack

Next-Plate A super template for React with Next.js and other incredible tools 🚀 Demo ❔ About This is a complete template all you need for your next r

All-in-one solution for configuring ESLint in all of your projects
All-in-one solution for configuring ESLint in all of your projects

✨ All-in-one solution for configuring ESLint in all of your projects ✨ Before you start The README on main branch can contain some unreleased changes.

Foot Locker is an online store specialized in selling shoes of all kinds, where you can find all the shoes you want
Foot Locker is an online store specialized in selling shoes of all kinds, where you can find all the shoes you want

Foot Locker Links 🖇 Heroku link Adobe XD link Description Foot Locker is an online store specialized in selling shoes of all kinds, where you can fin

The monorepo home to all of the FormatJS related libraries, most notably react-intl.

FormatJS This repository is the home of FormatJS and related libraries. Slack: Join us on Slack at formatjs.slack.com for help, general conversation a

📈 One library to rule all charts for React Native 📊
📈 One library to rule all charts for React Native 📊

react-native-svg-charts Welcome to react-native-svg-charts! react-native-svg-charts provides SVG Charts support to React Native on iOS and Android, an

Native CardView for react-native (All Android version and iOS)
Native CardView for react-native (All Android version and iOS)

react-native-card-view Native CardView that compatible for iOS and Android( both lollipop and pre-lolipop). Website Material Design Card Spec CardView

🎵 The frontend of Oud,  an online music streaming service that is a high quality clone of Spotify with all its functionalities built using ReactJS, React-Router, Bootstrap.
🎵 The frontend of Oud, an online music streaming service that is a high quality clone of Spotify with all its functionalities built using ReactJS, React-Router, Bootstrap.

Oud Front-End Table of Contents About the Project Build with Getting Started Installation Running Screenshots Videos File Structure Unit Testing Runni

Spotify, Soundcloud, and YouTube all in one website!
Spotify, Soundcloud, and YouTube all in one website!

Kord App Kord: One place for all of your (Spotify | Soundcloud | Youtube) music. Chromium and Firefox browsers supported only. Built With React.js Gat

Simple and lightweight multiple selection dropdown component with checkboxes, search and select-all
Simple and lightweight multiple selection dropdown component with checkboxes, search and select-all

Lightweight (~5KB gzipped) multiple selection dropdown component

Comments
  • Task/initial release

    Task/initial release

    • Fix rules not applying recursively in expressions with &&(and) or ||(or)
    • Added more test cases
    • Fix only checking identifiers and not other types of expressions
    • Sped up lint by removing unnecessary regular expression
    • Updated packages versions
    • Added build/test to pre-commit
    • Separated code into src folder
    opened by arthurgeron 0
Releases(0.1.0)
  • 0.1.0(Aug 8, 2022)

    • Create specific prop referential equality error messages for class components
    • Added class components examples on README
    • Disable linting memo JSX children on class components
    • Auto publish fixes
    • Added auto tag
    • Fixed husky not executing properly
    • Added commitlint
    • Enabled Jest Coverage
    Source code(tar.gz)
    Source code(zip)
  • 0.0.3(Jul 22, 2022)

  • 0.0.2(Jul 21, 2022)

  • 0.0.1(Jul 21, 2022)

    • Fix rules not applying recursively in expressions with &&(and) or ||(or)
    • Added more test cases
    • Fix only checking identifiers and not other types of expressions
    • Sped up lint by removing unnecessary regular expression
    • Updated packages versions
    • Added build/test to pre-commit
    • Separated code into src folder
    Source code(tar.gz)
    Source code(zip)
Owner
Arthur Geron
mostly active on my work profile: arthurgeron-work
Arthur Geron
😍 All kinds of resources for Developers 🔱 in one place.

?? All kinds of resources for Developers ?? in one place.

web Developer Community 624 Sep 23, 2022
⚡️The Fullstack React Framework — built on Next.js

⚡️The Fullstack React Framework — built on Next.js

⚡️Blitz 12.1k Sep 28, 2022
⚡️The Fullstack React Framework — built on Next.js — Inspired by Ruby on Rails

⚡️The Fullstack React Framework — built on Next.js — Inspired by Ruby on Rails

⚡️Blitz 9.4k Oct 12, 2021
UsePureCallback - useCallback doing right for react

usePureCallback This is not a NPM package, this is simple snipet to copy paste i

Arutyunyan Artyom 16 May 5, 2022
Same as React's useCallback, but returns a stable reference.

react-use-event-hook Same as React's useCallback, but returns a stable reference. This library is a user-land implementation of the useEvent hook, pro

Scott Rippey 124 Sep 21, 2022
Ethbio - Ethbio- The Bio Page for all things Crypto, NFTs, Social Pages and beyond

Ethbio Github | Screenshots | Discord Server(Soon) What's Ethbio? We've all seen

null 8 Apr 5, 2022
Mealdrop is a reproduction of a real-world application to serve as an example for all the cool things you can do with Storybook

Mealdrop is a reproduction of a real-world application to serve as an example for all the cool things you can do with Storybook

Yann Braga 29 Sep 2, 2022
✍ It has never been so easy to document your things!

Docz makes it easy to write and publish beautiful interactive documentation for your code. Create MDX files showcasing your code and Docz turns them i

Docz 22.9k Sep 26, 2022
A modular toolkit to build calendar-related things in React

react-calendar Calendars for React v15. Not just calendar component, but a modular toolkit for building everything related to calendars in React, such

Mikhail Novikov 250 Jun 17, 2022
:camera: Point your camera at things to learn how to say them in a different language. Android app built with React Native.

What the Thing ? Point camera at things to learn how to say them in a different language. Native Android App built with React Native. Made it as a par

Vignesh M 533 Sep 4, 2022