Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@arthurgeron/eslint-plugin-react-usememo

Package Overview
Dependencies
Maintainers
1
Versions
46
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@arthurgeron/eslint-plugin-react-usememo - npm Package Compare versions

Comparing version 1.1.4 to 2.0.0

254

dist/index.js

@@ -40,2 +40,71 @@ 'use strict';

var _a, _b, _c, _d;
var jsxEmptyExpressionClassData = (_a = {},
_a[MemoStatus.UnmemoizedObject.toString()] = "object-class-memo-props",
_a[MemoStatus.UnmemoizedArray.toString()] = "array-class-memo-props",
_a[MemoStatus.UnmemoizedNew.toString()] = "instance-class-memo-props",
_a[MemoStatus.UnmemoizedFunction.toString()] = 'instance-class-memo-props',
_a[MemoStatus.UnmemoizedFunctionCall.toString()] = "unknown-class-memo-props",
_a[MemoStatus.UnmemoizedOther.toString()] = "unknown-class-memo-props",
_a[MemoStatus.UnsafeLet.toString()] = "usememo-const",
_a);
var jsxEmptyExpressionData = (_b = {},
_b[MemoStatus.UnmemoizedObject.toString()] = "object-usememo-props",
_b[MemoStatus.UnmemoizedArray.toString()] = "array-usememo-props",
_b[MemoStatus.UnmemoizedNew.toString()] = "instance-usememo-props",
_b[MemoStatus.UnmemoizedFunction.toString()] = "function-usecallback-props",
_b[MemoStatus.UnmemoizedFunctionCall.toString()] = "unknown-usememo-props",
_b[MemoStatus.UnmemoizedOther.toString()] = "unknown-usememo-props",
_b[MemoStatus.UnmemoizedJSX.toString()] = "jsx-usememo-props",
_b[MemoStatus.UnsafeLet.toString()] = "usememo-const",
_b);
var hookReturnExpressionData = (_c = {},
_c[MemoStatus.UnmemoizedObject.toString()] = "object-usememo-hook",
_c[MemoStatus.UnmemoizedArray.toString()] = "array-usememo-hook",
_c[MemoStatus.UnmemoizedNew.toString()] = "instance-usememo-hook",
_c[MemoStatus.UnmemoizedFunction.toString()] = "function-usecallback-hook",
_c[MemoStatus.UnmemoizedFunctionCall.toString()] = "unknown-usememo-hook",
_c[MemoStatus.UnmemoizedOther.toString()] = "unknown-usememo-hook",
_c[MemoStatus.UnmemoizedJSX.toString()] = "jsx-usememo-hook",
_c[MemoStatus.UnsafeLet.toString()] = "usememo-const",
_c);
var callExpressionData = (_d = {},
_d[MemoStatus.UnmemoizedObject.toString()] = "object-usememo-deps",
_d[MemoStatus.UnmemoizedArray.toString()] = "array-usememo-deps",
_d[MemoStatus.UnmemoizedNew.toString()] = "instance-usememo-deps",
_d[MemoStatus.UnmemoizedFunction.toString()] = "function-usecallback-deps",
_d[MemoStatus.UnmemoizedFunctionCall.toString()] = "unknown-usememo-deps",
_d[MemoStatus.UnmemoizedOther.toString()] = "unknown-usememo-deps",
_d[MemoStatus.UnmemoizedJSX.toString()] = "jsx-usememo-deps",
_d[MemoStatus.UnsafeLet.toString()] = "usememo-const",
_d);
var defaultReactHookNames = {
"useContext": true,
"useState": true,
"useReducer": true,
"useRef": true,
"useLayoutEffect": true,
"useEffect": true,
"useImperativeHandle": true,
"useCallback": true,
"useMemo": true,
"useDebugValue": true,
"useDeferredValue": true,
"useTransition": true,
"useId": true,
"useInsertionEffect": true,
"useSyncExternalStore": true,
"useQuery": true,
"useMutation": true,
"useQueryClient": true,
"useInfiniteQuery": true
};
var messageIdToHookDict = {
'function-usecallback-props': 'useCallback',
'function-usecallback-hook': 'useCallback',
'function-usecallback-deps': 'useCallback',
'object-usememo-props': 'useMemo',
'usememo-const': 'useMemo'
};
function shouldIgnoreNode(node, ignoredNames) {

@@ -62,3 +131,4 @@ var _a, _b;

if (node.type === "Identifier") {
return node.name[0] === 'u' && node.name[1] === 's' && node.name[2] === 'e';
var name_1 = node.name;
return name_1[0] === 'u' && name_1[1] === 's' && name_1[2] === 'e';
}

@@ -68,3 +138,3 @@ else if (node.type === "MemberExpression" &&

getIsHook(node.property)) {
var obj = node.object;
var obj = node.object; // Utilizing Object destructuring
return obj.type === "Identifier" && obj.name === "React";

@@ -76,2 +146,107 @@ }

}
// Helper function to find parent of a specified type.
function findParentType(node, type) {
var parent = node.parent;
while (parent) {
if (parent.type === type)
return parent;
parent = parent.parent;
}
return undefined;
}
function fixFunction(node, context, shouldSetName) {
var _a, _b;
var sourceCode = context.getSourceCode();
var body = node.body, _c = node.params, params = _c === void 0 ? [] : _c;
var funcBody = sourceCode.getText(body);
var funcParams = params.map(function (node) { return sourceCode.getText(node); });
var fixedCode = "React.useCallback((".concat(funcParams.join(', '), ") => ").concat(funcBody, ", [])").concat(shouldSetName ? ';' : '');
if (shouldSetName && ((_a = node === null || node === void 0 ? void 0 : node.id) === null || _a === void 0 ? void 0 : _a.name)) {
var name_2 = (_b = node === null || node === void 0 ? void 0 : node.id) === null || _b === void 0 ? void 0 : _b.name;
fixedCode = "const ".concat(name_2, " = ").concat(fixedCode);
}
return fixedCode;
}
function getSafeVariableName(context, name) {
var tempVarPlaceholder = 'renameMe';
if (!getVariableInScope(context, name)) {
return name;
}
if (!getVariableInScope(context, "_".concat(name))) {
return "_".concat(name);
}
return tempVarPlaceholder;
}
// Eslint Auto-fix logic, functional components/hooks only
function fixBasedOnMessageId(node, messageId, fixer, context) {
var _a, _b, _c, _d;
var sourceCode = context.getSourceCode();
var hook = messageIdToHookDict[messageId] || 'useMemo';
var isObjExpression = node.type === 'ObjectExpression';
var parentIsVariableDeclarator = node.parent.type === 'VariableDeclarator';
var isArrowFunctionExpression = node.type === 'ArrowFunctionExpression';
var isFunctionExpression = node.type === 'FunctionExpression';
var isCorrectableFunctionExpression = isFunctionExpression || (isArrowFunctionExpression && parentIsVariableDeclarator);
// Determine what type of behavior to follow according to the error message
switch (messageId) {
case 'function-usecallback-props':
case 'object-usememo-props':
case 'usememo-const': {
var sourceCode_1 = context.getSourceCode();
var variableDeclaration = node.type === 'VariableDeclaration' ? node : findParentType(node, 'VariableDeclaration');
var fixes = [];
// Check if it is a hook being stored in let/var, change to const if so
if ((variableDeclaration === null || variableDeclaration === void 0 ? void 0 : variableDeclaration.kind) !== 'const') {
var tokens = sourceCode_1.getTokens(variableDeclaration);
var letKeywordToken = tokens === null || tokens === void 0 ? void 0 : tokens[0];
if ((letKeywordToken === null || letKeywordToken === void 0 ? void 0 : letKeywordToken.value) !== 'const') {
fixes.push(fixer.replaceTextRange(letKeywordToken.range, 'const'));
}
}
// If it's an dynamic object - Add useMemo/Callback
if ((isObjExpression || isCorrectableFunctionExpression)) {
var fixed_1 = isCorrectableFunctionExpression ? fixFunction(node, context) : "React.useMemo(() => (".concat(sourceCode_1.getText(node), "), [])");
var parent_1 = node.parent;
// Means we have a object expression declared directly in jsx
if (parent_1.type === 'JSXExpressionContainer') {
var parentPropName = (_b = (_a = parent_1 === null || parent_1 === void 0 ? void 0 : parent_1.parent) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.name.toString();
var newVarName = getSafeVariableName(context, parentPropName);
var returnStatement = findParentType(node, 'ReturnStatement');
if (returnStatement) {
var indentationLevel = sourceCode_1.lines[returnStatement.loc.start.line - 1].search(/\S/);
var indentation = ' '.repeat(indentationLevel);
// Creates a declaration for the variable and inserts it before the return statement
fixes.push(fixer.insertTextBeforeRange(returnStatement.range, "const ".concat(newVarName, " = ").concat(fixed_1, ";\n").concat(indentation)));
// Replaces the old inline object expression with the variable name
fixes.push(fixer.replaceText(node, newVarName));
}
}
else {
fixes.push(fixer.replaceText(node, fixed_1));
}
}
return !fixes.length ? null : fixes;
}
// Unknown cases are usually complex issues or false positives, so we ignore them
case 'unknown-class-memo-props':
case 'unknown-usememo-hook':
case 'unknown-usememo-deps':
case 'unknown-usememo-props':
return null;
}
// Simpler cases bellow, all of them are just adding useMemo/Callback
var fixed = "React.".concat(hook, "(() => ").concat(isObjExpression ? "(" : '').concat(sourceCode.getText(node)).concat(isObjExpression ? ")" : '', ", [])");
if (node.type === 'FunctionDeclaration') {
var _node = node;
if (_node && ((_c = _node === null || _node === void 0 ? void 0 : _node.id) === null || _c === void 0 ? void 0 : _c.type) === "Identifier") {
fixed = fixFunction(_node, context, true);
}
}
if ('computed' in node && ((_d = node === null || node === void 0 ? void 0 : node.computed) === null || _d === void 0 ? void 0 : _d.type) === 'ArrowFunctionExpression') {
return fixer.replaceText(node.computed, fixed);
}
else {
return fixer.replaceText(node, fixed);
}
}

@@ -108,6 +283,9 @@ var componentNameRegex = /^[^a-z]/;

}
function getVariableInScope(context, name) {
return context.getScope().variables.find(function (variable) { return variable.name === name; });
}
function getIdentifierMemoStatus(context, _a) {
var _b, _c, _d, _e, _f;
var name = _a.name;
var variableInScope = context.getScope().variables.find(function (v) { return v.name === name; });
var variableInScope = getVariableInScope(context, name);
if (variableInScope === undefined)

@@ -254,64 +432,2 @@ return { status: MemoStatus.Memoized };

var _a, _b, _c, _d;
var jsxEmptyExpressionClassData = (_a = {},
_a[MemoStatus.UnmemoizedObject.toString()] = "object-class-memo-props",
_a[MemoStatus.UnmemoizedArray.toString()] = "array-class-memo-props",
_a[MemoStatus.UnmemoizedNew.toString()] = "instance-class-memo-props",
_a[MemoStatus.UnmemoizedFunction.toString()] = 'instance-class-memo-props',
_a[MemoStatus.UnmemoizedFunctionCall.toString()] = "unknown-class-memo-props",
_a[MemoStatus.UnmemoizedOther.toString()] = "unknown-class-memo-props",
_a[MemoStatus.UnsafeLet.toString()] = "usememo-const",
_a);
var jsxEmptyExpressionData = (_b = {},
_b[MemoStatus.UnmemoizedObject.toString()] = "object-usememo-props",
_b[MemoStatus.UnmemoizedArray.toString()] = "array-usememo-props",
_b[MemoStatus.UnmemoizedNew.toString()] = "instance-usememo-props",
_b[MemoStatus.UnmemoizedFunction.toString()] = "function-usecallback-props",
_b[MemoStatus.UnmemoizedFunctionCall.toString()] = "unknown-usememo-props",
_b[MemoStatus.UnmemoizedOther.toString()] = "unknown-usememo-props",
_b[MemoStatus.UnmemoizedJSX.toString()] = "jsx-usememo-props",
_b[MemoStatus.UnsafeLet.toString()] = "usememo-const",
_b);
var hookReturnExpressionData = (_c = {},
_c[MemoStatus.UnmemoizedObject.toString()] = "object-usememo-hook",
_c[MemoStatus.UnmemoizedArray.toString()] = "array-usememo-hook",
_c[MemoStatus.UnmemoizedNew.toString()] = "instance-usememo-hook",
_c[MemoStatus.UnmemoizedFunction.toString()] = "function-usecallback-hook",
_c[MemoStatus.UnmemoizedFunctionCall.toString()] = "unknown-usememo-hook",
_c[MemoStatus.UnmemoizedOther.toString()] = "unknown-usememo-hook",
_c[MemoStatus.UnmemoizedJSX.toString()] = "jsx-usememo-hook",
_c[MemoStatus.UnsafeLet.toString()] = "usememo-const",
_c);
var callExpressionData = (_d = {},
_d[MemoStatus.UnmemoizedObject.toString()] = "object-usememo-deps",
_d[MemoStatus.UnmemoizedArray.toString()] = "array-usememo-deps",
_d[MemoStatus.UnmemoizedNew.toString()] = "instance-usememo-deps",
_d[MemoStatus.UnmemoizedFunction.toString()] = "function-usecallback-deps",
_d[MemoStatus.UnmemoizedFunctionCall.toString()] = "unknown-usememo-deps",
_d[MemoStatus.UnmemoizedOther.toString()] = "unknown-usememo-deps",
_d[MemoStatus.UnmemoizedJSX.toString()] = "jsx-usememo-deps",
_d[MemoStatus.UnsafeLet.toString()] = "usememo-const",
_d);
var defaultReactHookNames = {
"useContext": true,
"useState": true,
"useReducer": true,
"useRef": true,
"useLayoutEffect": true,
"useEffect": true,
"useImperativeHandle": true,
"useCallback": true,
"useMemo": true,
"useDebugValue": true,
"useDeferredValue": true,
"useTransition": true,
"useId": true,
"useInsertionEffect": true,
"useSyncExternalStore": true,
"useQuery": true,
"useMutation": true,
"useQueryClient": true,
"useInfiniteQuery": true
};
var MessagesRequireUseMemo = {

@@ -361,2 +477,3 @@ "object-usememo-props": "Object literal should be wrapped in useMemo() or be static when used as a prop",

},
fixable: 'code',
schema: [

@@ -373,3 +490,8 @@ {

function report(node, messageId) {
context.report({ node: node, messageId: messageId });
context.report({ node: node, messageId: messageId, fix: function (fixer) {
if (isClass) {
return null;
}
return fixBasedOnMessageId(node, messageId, fixer, context);
} });
}

@@ -376,0 +498,0 @@ function process(node, _expression, expressionData) {

{
"name": "@arthurgeron/eslint-plugin-react-usememo",
"version": "1.1.4",
"version": "2.0.0",
"description": "",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

@@ -1,330 +0,91 @@

# eslint-plugin-react-usememo
# 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.
This plugin enforces the wrapping of complex objects or functions (which might generate unnecessary renders or side-effects) in `useMemo` or `useCallback`. It also allows you to programmatically enforce the wrapping of functional components in `memo`, and that all props and dependencies are wrapped in `useMemo`/`useCallback`.
## Rationale
React Native's own [docs](https://reactnative.dev/docs/0.61/optimizing-flatlist-configuration#avoid-anonymous-function-on-renderitem) 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.
## Purpose
The objective is to ensure that your application's component tree and/or expensive lifecycles (such as React Native's FlatLists, useEffect, useMemo, etc.) only re-calculate or render again when absolutely necessary. By controlling expensive expressions, you can achieve optimal scalability and performance for your application.
_**Note:**_ Use of memoization everywhere is not advised, as everything comes with a cost. Overusing memoization might slow down your application instead of speeding it up.
# Installation
## Guidelines for Memoization
```
yarn add @arthurgeron/eslint-plugin-react-usememo --dev
```
or
```
npm install @arthurgeron/eslint-plugin-react-usememo --save-dev
```
Here are two primary rules for situations where dynamic objects should be memoed:
1. Variables or expressions that return non-primitive objects or functions passed as props to other components.
2. Variables or expressions that return non-primitive objects returned from custom hooks.
# Usage
It is not recommended to use memoization in the following cases:
To enable the plugin add the following to the `plugin` property your `eslintrc` file:
```json
"plugins": ["@arthurgeron/react-usememo"],
```
- When the resulting value (expression or variable) is primitive (string, number, boolean).
- If you're passing props to a native component of the framework (e.g. Div, Touchable, etc), except in some instances in react-native (e.g. FlatList).
- Values that can be a global/context outside the react Context.
Then enable any rules as you like, example:
```json
"rules": {
"@arthurgeron/react-usememo/require-usememo": [2],
},
```
# Rules
Example for better understanding:
## `require-usememo` **Recommended**
***Incorrect***
```js
function Component() {
const breakpoints = [100];
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.);
- `{checkHookReturnObject: true}`: Will require Object Expressions passed in return statements (e.g. `return {someFunc}`) to also be memoised (e.g. `return useMemo(() => ({someFunc}), [someFunc])`); **Disabled** by default;
- `{checkHookCalls: true}`: Will require objects/data passed to a non-native/Custom hook to be memoized (e.g. in this code `const data = useParse(unparsedData)` it will check `unparsedData` for memo status), this check can be ignored by a hook/name basis, check next item for details; **Enabled** by default;
- `{ignoredHookCallsNames: Record<string, boolean>}`: You can add specific hooks names here, individually disabling or enabling them to be checked when used, e.g. if you have a custom hook named `useX` and it should be called with unmemoized parameters you can set `{ ignoredHookCallsNames: { useX: false } }` and then have somethning like `const data = {}; const x = useX(data);` not generate any errors.
### Function Components
#### **Incorrect**
```JavaScript
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 ?? []} />);
}
return <Modal breakpoints={breakpoints}>
}
```
#### **Correct**
```JavaScript
// 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***
```js
const breakpoints = [100];
function Component() {
return <Modal breakpoints={breakpoints}>
}
```
### Class Components
#### **Incorrect**
```JavaScript
class Component() {
> For more details, please refer to React Native's [documentation](https://reactnative.dev/docs/0.61/optimizing-flatlist-configuration#avoid-anonymous-function-on-renderitem) on the importance of using static or memoized props for complex children.
constructor(props) {
super(props);
this.state = {
data: undefined,
propDrivenData: props.,
};
}
// This will NOT be redeclared each render
getItemName(item) {
return item.name;
}
## Installation
render() {
// This function will be redeclared each render
function renderItem({ item }) {
return (<Text>{this.getItemName(item)}</Text>);
}
Install it with yarn:
// 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**
```JavaScript
// 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} />);
}
}
yarn add @arthurgeron/eslint-plugin-react-usememo --dev
```
#### **Correct**
```JavaScript
const EMPTY_ARRAY = [];
function Component() {
const [data, setData] = useState(EMPTY_ARRAY);
const [isEditing, setIsEditing] = useState(false);
or npm:
// 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} />);
}
```
### Hooks
Hooks return statements follow the same motto, they can and usually are used inside other hooks.
#### **Incorrect**
```JavaScript
function useData() {
let otherData = {};
function getData() {}
return { getData, otherData};
}
npm install @arthurgeron/eslint-plugin-react-usememo --save-dev
```
#### **Incorrect** (checkHookReturnObject: true)
```JavaScript
function getData() {}
function useData() {
return { getData };
}
```
## Usage
#### **Incorrect** Calling a custom hook
```JavaScript
function doSomething() {}
function useStateManagement(someFlag) {
useEffect(() => {
doSomething(someFlag);
}, [someFlag]);
}
function useData() {
const someFlag = {};
// Some flag is memoized, so not unwanted side effects are generated in the other hook
const data = useStateManagement(someFlag);
}
Add the plugin to your `eslintrc` file:
```json
"plugins": ["@arthurgeron/react-usememo"],
```
#### **Correct**
```JavaScript
const otherData = {}; // Or declare inside hook with useMemo
Then enable any rules as you like:
function useData() {
const getData = useCallback(() => { // Or declare statically
}, []);
return {getData, otherData};
}
```json
"rules": {
"@arthurgeron/react-usememo/require-usememo": [2],
},
```
In this guide, we will cover three rules - `require-usememo`, `require-memo`, and `require-usememo-children`.
#### **Correct** (checkHookReturnObject: true)
```JavaScript
function getData() {
// Doing something
}
function useData() {
return useMemo(() => ({ getData }), []);
}
```
#### **Correct** Passing a dependency to a hook
```JavaScript
function getData() {}
## Rule #1: `require-usememo` ***(recommended)***
This rule requires complex values (objects, arrays, functions, and JSX) that get passed props or referenced as a hook dependency to be wrapped in useMemo() or useCallback().
function useData() {
return useMemo(() => ({ getData }), []);
}
```
#### **Correct** Calling a custom hook
```JavaScript
function doSomething() {}
One of the great features of this rule is its amazing autofix functionality. It intelligently wraps necessary components with useMemo() or useCallback(), making your code more efficient and saving you valuable time.
function useStateManagement(someFlag) {
useEffect(() => {
doSomething(someFlag);
}, [someFlag]);
}
For detailed examples, options available for this rule, and information about the autofix functionality, please refer to our rules documentation.
function useData() {
const someFlag = useMemo(() => ({}), []);
// Some flag is memoized, so not unwanted side effects are generated in the other hook
const data = useStateManagement(someFlag);
}
```
#### **"Correct"** Calling a custom hook (checkHookCalls: true) or (ignoredHookCallsNames: { useStateManagement: false })
```JavaScript
function doSomething() {
}
function useStateManagement(someFlag) {
useEffect(() => {
doSomething(someFlag);
}, [someFlag]);
}
## Rule #2: `require-memo`
This rule requires all function components to be wrapped in `React.memo()`.
function useData() {
const someFlag = useMemo(() => ({}), []);
// Some flag is regenerated each render, hence useEffect in "useStateManagement" will execute a new cycle each render
// Either way this will not generate a error/warning because of the options passed
const data = useStateManagement(someFlag);
}
```
For detailed examples and usage of this rule, please refer to our [rules documentation](https://github.com/arthurgeron/eslint-plugin-react-usememo/blob/main/docs/rules/require-memo.md)
## Rule #3: `require-usememo-children`
This rule requires complex values (objects, arrays, functions, and JSX) that get passed as children to be wrapped in `useMemo()` or `useCallback()`.
For detailed examples and options available for this rule, please refer to our [rules documentation](https://github.com/arthurgeron/eslint-plugin-react-usememo/blob/main/docs/rules/require-usememo-children.md).
## `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**
```JavaScript
export default function Component() {
return (<Text>This is a component</Text>);
}
```
## **Correct**
```JavaScript
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**
```JavaScript
function Component() {
return (<View>
<>
<OtherComponent />
</>
</View>);
}
```
## **Correct**
```JavaScript
function Component() {
const children = useMemo(() => (<OtherComponent />), []);
return (<View>
{children}
</View>);
}
```
## Conclusion
By efficiently using `useMemo`, `useCallback`, and `React.memo()`, we can optimize our React and React Native applications. It allows us to control the re-calculation and re-rendering of components, offering better scalability and performance.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc