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

react-native-best-practice

v0.1.6

Published

best pactices of react native

Downloads

19

Readme

GitHub Repo stars GitHub Repo stars GitHub Repo stars GitHub Repo stars npm

REACT-NATIVE-BEST-PRACTICE is a react-native best practice and a package, which provides techniques for increasing performance as well as utilities for increasing the performance of the app

Table of Contents

Why-Did-You-Render (avoid extra rerendering)

you should use why-did-you-render which notify you about potentially avoidable re-renders.

import React from 'react';

const useWDYR = __DEV__;

if (useWDYR) {
  const whyDidYouRender = require('@welldone-software/why-did-you-render');
  whyDidYouRender(React, {
    // Enable tracking in all pure components by default
    trackAllPureComponents: true,
    trackHooks: true,

    include: [
      // Uncomment to enable tracking in all components. Must also uncomment /^Screen/ in exclude.
      // /.*/,
      // Uncomment to enable tracking by displayName, e.g.:
      // /^Components/,
    ],

    exclude: [
      // Uncomment to enable tracking in all components
      // /^Components/,
    ],
  });
}

import above file code into your app root index.js

Memoization in React

It's important to memoize heavy computations as well as arrays and object creations so that they don't get re-created on every render. A re-render occurs when state changes, redux dispatches some action, or when the user types into a text input (re-render for every single key press). You don't want to run a lot of operations in those renders for very obvious reasons - so no heavy filtering, no list operations, etc.

Pure Components

A Pure Component (or a React.memo component) does not re-render if it's props are shallow equal.

Each variable you create in your render function will get re-allocated on each render. While this is not a problem for value types, this causes reference types to be different on every render. When you pass those variables down to pure components via props, they will still re-render even though the props are logically the same. Often those variables even go over the Bridge and make your app slow.

Reference equality

When a pure component re-renders, it compares the previous props to the current props and checks if they are shallow-equal.

Value types

Numbers, strings and booleans are value types, which means they can be compared by value:

const i1 = 7;
const i2 = 7;
const equal = i1 === i2; // true

Reference types

Objects, arrays and functions are reference types, which means they cannot be compared by their logical value, but have to be compared by reference:

const o1 = { x: 7 };
const o2 = { x: 7 };
const equal = o1 === o2; // false

Reference comparisons simply compare the memory address of the variable, so only o1 === o1 would be true in the above code example.

'isEqualfromreact-native-best-practice` to compare objects by actual equality, but that's not shallow equality anymore.

React

If you create objects in your render function, they will be re-created on every single render. This means when you create an object in the first render, it is not reference-equal to the object in the second render. For this very reason, memoization exists.

  • Use the useMemo hook to memoize arrays and objects which will keep their reference equality (and won't get re-created on each render) as long as the dependencies (second argument) stay the same. Also use useMemo to cache heavy computations, such as array operations, filtering, etc.
  • Use the useCallback hook to memoize a function.
  • If you are re creating objective but can have same values then you can use useDeepEffect, useDeepCallback, useDeepImperativeHandle and useDeepLayoutEffect from import {useDeepEffect, ...} from 'react-native-best-practice'

In general, function components can be optimized more easily due to the concept of hooks. You can however apply similar techniques for class components, just be aware that this will result in a lot more code.

React Native

While animations and performance intensive tasks are scheduled on native threads, your entire business logic runs on a single JavaScript thread, so make sure you're doing as little work as possible there. Doing too much work on the JavaScript thread can be compared to a high ping in a video game - you can still look around smoothly, but you can't really play the game because every interaction takes too long.

Native components (<View>, <Text>, <Image>, <Blurhash>, ...) have to pass props to native via the bridge. They can be memoized, so React compares the props for shallow-equality and only passes them over the bridge if they are different than the props from the last render. If you don't memoize correctly, you might up passing props over the bridge for every single render, causing the bridge to be very occupied. See the Styles example - styles will get sent over the bridge on every re-render!

Here are a few examples to help you avoid doing too much work on your JavaScript thread:

Examples

Styles

Bad

return <View style={[styles.container, { backgroundColor: 'red' }]} />;

Good

const style = useMemo(() => [styles.container, { backgroundColor: 'red' }], []);
return <View style={style} />;

Exceptions

  • Reanimated styles from useAnimatedStyle, as those have to be dynamic.

Arrays

Using filter, map or other array operations in renderers will run the entire operation again for every render.

Bad

return (
  <Text>{users.filter((u) => u.status === 'online').length} users online</Text>
);

Good

const onlineCount = useMemo(
  () => users.filter((u) => u.status === 'online').length,
  [users]
);
return <Text>{onlineCount} users online</Text>;

You can also apply this to render multiple React views with .map. Those can be memoized with useMemo too.

Functions

Bad

return <View onLayout={(layout) => console.log(layout)} />;

Good

const onLayout = useCallback((layout) => {
  console.log(layout);
}, []);
return <View onLayout={onLayout} />;

Make sure to also think about other calls in the renderer, e.g. useSelector, useComponentDidAppear - wrap the callback there too!

Forward-propagating Functions

Bad

function MyComponent(props) {
  return <PressableOpacity onPress={() => props.logoutUser()} />;
}

Good

function MyComponent(props) {
  return <PressableOpacity onPress={props.logoutUser} />;
}

Objects

Bad

function MyComponent(props) {
  return (
    <RecyclerListView scrollViewProps={{ horizontal: props.isHorizontal }} />
  );
}

Good

function MyComponent(props) {
  const scrollViewProps = useMemo(
    () => ({
      horizontal: props.isHorizontal,
    }),
    [props.isHorizontal]
  );
  return <RecyclerListView scrollViewProps={scrollViewProps} />;
}

Lift out of render

Bad

function MyComponent() {
  return <RecyclerListView scrollViewProps={{ horizontal: true }} />;
}

Good

const SCROLL_VIEW_PROPS = { horizontal: true };

function MyComponent() {
  return <RecyclerListView scrollViewProps={SCROLL_VIEW_PROPS} />;
}

This applies to objects as well as functions which don't depend on the component's state or props. Always use this if you can, since it's even more efficient than useMemo and useCallback.

Initial States

Bad

const [me, setMe] = useState(users.find((u) => u.id === myUserId));

Good

const [me, setMe] = useState(() => users.find((u) => u.id === myUserId));

The useState hook accepts an initializer function. While the first example ("Bad") runs the .find on every render, the second example only runs the passed function once to initialize the state.

Count re-renders

When writing new components I always put a log statement in my render function to passively watch how often my component re-renders while I'm working on it. In general, components should re-render as little as possible, and if I see a lot of logs appearing in my console I know I did something wrong. It's a good pactice to put this function in your component once you start working on it, and remove it once done.

function ComponentImWorkingOn() {
  // code
  console.log('re-rendering ComponentImWorkingOn!');
  return <View />;
}

You can also use the why-did-you-render library to find out why a component has re-rendered (prop changes, state changes, ...) and possibly catch mistakes early on.

React.memo

Bad

export const MyComponent = (props) => {
  return ...
}

Good

const MyComponentImpl = (props) => {
  return ...
}

export const MyComponent = React.memo(MyComponentImpl);

If your component renders the same result given the same props, you can wrap it in a call to React.memo(...) for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result. See the official docs for React.memo, and use React.memo(...) wisely.

react-native-performance

If your app feels slow, try the react-native-performance library and it's flipper plugin to profile your app's performance in various aspects such as time to interactive, component render time, script execution and more.

Disclaimer

Don't prematurely optimize. Some examples used here (e.g. the useMemo one) are very small and only demonstrate the idea. A hook like `useMemo also comes with a cost (allocating the function and the deps array, calling the actual hook and running an array comparison), so keep in mind that it is often better to just pass in objects or arrays directly if the component itself is optimized. After a certain component complexity or with a certain dependency graph, memoizing functions can be a huge performance win, but there are also cases where it just leads to unnecessarily complex code and sometimes even worse performance. Always benchmark before and after!

best-practices hooks and utils

Installation

npm i react-native-best-practice

Usage

All passing arguments as in native React Hooks like

import {useDeepEffect} from 'react-native-best-practice'

useDeepEffect(()=>{

},[recreatedDeepObject])

Hooks list

  • useDeepEffect => useEffect
  • useDeepMemo => useMemo
  • useDeepCallback => useCallback
  • useDeepImperativeHandle => useImperativeHandle
  • useDeepLayoutEffect => useLayoutEffect

Util

  • isEqual => will check isEqual deeply
  • cloneDeep => will clone object and array deeply

Pro tips

Monitor RAM, JS framerate, and UI framerate

Always turn on Pref monitor while developing the app as it will tell you UI framerate and JS framerate, if any frame drops then you can check which new code is causing for dropping frame and making your Time To Interactive (TTI) low, you can open up the Dev Menu in your app and toggle Show Perf Monitor.

Use Flashlist for listing

never use flatlist, always use flashlist as it uses the concept of recycling views, which only create and render a limited number of views that are visible on the screen

https://github.com/numandev1/react-native-best-practice/assets/36044436/e20bbe5c-da08-4242-a89c-94a31635ac65

Custom Logger

Create a custom Logger instance and enforce emojis to categorize logs. This way it's easier to spot relevant lines in your console, and looks more friendly overall. Do lots of logging to save yourself from long nights of debugging at a later point!

Native Stack

Always use the native-stack from @reactnavigation . Since it uses platform-native screen primitives, it is almost always worth the performance gains over the JS-based stack.

State Management

  • Redux if you are already using redux then you should use reselect for memoized "selector" functions

  • React Context if you are already using react context then you should use use-context-selector for memoized "selector" functions otherwise a context value is changed, all components that useContext will re-render.

recommended to use these libs Recoil, Jotai Zustand for state management

Ref

if you need to do something in child component from parent component, use ref and useImperativeHandle for calling child components functions from parent component instead of playing with state passing into child component

Buffer

Use the react-native-buffer fork from @craftzdog, when dealing with lots of Buffers to speed up your app. It uses C++ backed implementations exposed through JSI, which is roughly 4x faster than the JS-based implementation.

Storage (MMKV)

Use react-native-mmkv to synchronously store and retrieve data from local storage, which persists even on the next app launch. Compared to localStorage on Web, MMKV also allows you to encrypt your data and have multiple instances!

Image/Video Placeholders

Use react-native-blurhash to show beautiful blurry placeholders for your images and videos. Generate a short string that represents a blurry version of your content ("blurhash") server-side and send it alongside with your data to the app!

Currencies Handle

When presenting numbers to the user, you should format them in the correct locale (commas, currency signs, ..) While .toLocaleString(..) does it's job, you can actually construct your own NumberFormat instance to improve performance by ~2x!

Safearea insets

Always handle Safe Area Insets appropriately. Instead of wrapping the entire screen in a , you can work with paddings to create cleaner UIs.

Here, we passed contentContainerStyle={{ paddingBottom: safeAreaBottom }} to the :

https://github.com/numandev1/react-native-best-practice/assets/36044436/d9e3acef-26a4-43be-b735-e814006532ed

C++ Book

Read "Effective Modern C++". Even if you're not a C++ developer, this book will help you understand how memory management works and believe it or not this affects how you think about React (Native). In fact that's the only programming book I ever read

you'll understand why {} === {} is false, how identity equality works, how re-renders are tons of allocations, how to avoid copies, and lots of other stuff about performance that just makes you think a little bit different when writing code. Just don't prematurely optimize 😄

And if you want to go into C++ development with JSI, it's even better that you read this book - C++ is not as forgiving as JS. If you make a library that has bad C++ code in it, users will hate you for making their app SIGABRT 😄

Custom Text Component

Never use the component directly. Instead, create your own abstraction so you don't repeat yourself with font name, font size or colors each time and it's easier to change properties at any point. Additionally, create an ESLint rule to warn you every time you're trying to use instead of ⭐️

Big Number

When dealing with large numbers, use react-native-bignumber instead of any of the JS-based libraries. ⚡️ It is backed by a pure C++ implementation and is ~330x faster than BN.js in certain applications (e.g. #crypto apps, ethers.js, elliptic, bitcoin)

Crypto

When working with #crypto / cryptography, use react-native-quick-crypto instead of any of the JS-based libraries. ⚡️ It is backed by a pure C++ implementation and is up to 58x faster than react-native-crypto or crypto-browserify in certain scenarios.

Do Measure Performance and Profiling

you can use FLASHLIGHT for generateing a performance score for your Android app

you can do Profiling for performance optimization.

Remove Console.log from production app

You should remove console.log from prod app as using console.log statements lowers the FPS, you can remove console.log by reading this

Credits

Thanks to Marc Rousavy and Margelo as mostly best practices are their