Skip to content

Instantly share code, notes, and snippets.

@naqvitalha
Last active April 16, 2024 02:46
Show Gist options
  • Save naqvitalha/fafd4f7946b18ecfa6a86b5ba65088e2 to your computer and use it in GitHub Desktop.
Save naqvitalha/fafd4f7946b18ecfa6a86b5ba65088e2 to your computer and use it in GitHub Desktop.
Batched setState
import React from 'react';
import {unstable_batchedUpdates} from 'react-native';
const originalUseState = React.useState;
export function createBatchedStateHook(
batchingFunction: (fn: () => void) => void,
) {
const useState = originalUseState;
let updateQueue: (() => void)[] = [];
let updateInProgress = false;
return <T>(initialValue: T | (() => T)) => {
const [state, setState] = useState(initialValue);
const batchedSetState: typeof setState = React.useCallback(value => {
updateQueue.push(() => {
setState(value);
});
if (!updateInProgress) {
updateInProgress = true;
batchingFunction(() => {
const tasks = updateQueue;
updateQueue = [];
unstable_batchedUpdates(() => {
tasks.forEach(task => task());
updateInProgress = false;
});
});
}
}, []);
return [state, batchedSetState] as const;
};
}
/**
* This hook is a drop-in replacement for `useState` that batches state updates.
* Batching can significantly improve performance when multiple state updates are required in rapid succession.
* Please note the setState returned by this hook is stable but may still trigger exhaustive deps warnings. You can add it to the deps array to silence the warning.
*/
export const useBatchedState = createBatchedStateHook(setTimeout);
/**
* This exposes default `useState` implementation. Useful if batched state updates are enabled globally which replaces the `useState` hook.
*/
export const useStateSync = originalUseState;
export function enableBatchedStateUpdates() {
Object.assign(React, {useState: useBatchedState});
}
@hirbod
Copy link

hirbod commented Mar 6, 2024

In certain configurations this snippet will fail because the generic <T> is interpreted as a JSX tag. Adding a comma to form <T,> could fix this, but this might conflict with numerous 'no-comma-dangle' rules. Therefore, a better solution is to simply return a named function, which resolves all TypeScript issues.

import React from 'react'
import { unstable_batchedUpdates } from 'react-native'

const originalUseState = React.useState

export function createBatchedStateHook(batchingFunction: (fn: () => void) => void) {
  const useState = originalUseState
  let updateQueue: (() => void)[] = []
  let updateInProgress = false

  return function SetBatchedState<T>(initialValue: T | (() => T)) {
    const [state, setState] = useState(initialValue)

    const batchedSetState: typeof setState = React.useCallback((value) => {
      updateQueue.push(() => {
        setState(value)
      })

      if (!updateInProgress) {
        updateInProgress = true

        batchingFunction(() => {
          const tasks = updateQueue
          updateQueue = []
          unstable_batchedUpdates(() => {
            tasks.forEach((task) => task())
            updateInProgress = false
          })
        })
      }
    }, [])

    return [state, batchedSetState] as const
  }
}

/**
 * This hook is a drop-in replacement for `useState` that batches state updates.
 * Batching can significantly improve performance when multiple state updates are required in rapid succession.
 * Please note the setState returned by this hook is stable but may still trigger exhaustive deps warnings. You can add it to the deps array to silence the warning.
 */
export const useBatchedState = createBatchedStateHook(setTimeout)

/**
 * This exposes default `useState` implementation. Useful if batched state updates are enabled globally which replaces the `useState` hook.
 */
export const useStateSync = originalUseState

export function enableBatchedStateUpdates() {
  Object.assign(React, { useState: useBatchedState })
}

@SrMouraSilva
Copy link

@naqvitalha, thank you for sharing this snippet. I have a question about it:

Did you have some improvement when using React >= 18? I read the shopify post but it isn't clear which React version does your team uses.
https://shopify.engineering/improving-shopify-app-s-performance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment