React hooks

React Hooks are powerful functions that enable state management and lifecycle handling within functional components-capabilities that were previously limited to class components. With hooks like useState for managing local state and useEffect for handling side effects, developers can write cleaner, more modular code. Hooks promote reusability by allowing you to encapsulate stateful logic, while adhering to specific rules - such as calling hooks only at the top level and always prefixing them with use- to maintain predictable and consistent behavior.

Below is a curated collection of useful React Hooks developed specifically for use across various Kommo projects, integrations, and widgets.

Installation

Installations options are available in the public GitHub repository.

npmyarnpnpm
npm i @kommo-crm/react-hooksyarn add @kommo-crm/react-hookspnpm add @kommo-crm/react-hooks

Hooks

useConst

This hook is designed to be used instead of useMemo with an empty array of dependencies

React hook that memoize the value returned by the fn function each time the Componentis called. It ensures that the value is saved between renderings of the component to avoid recalculating and recreating the value each time the component is updated.

Usage

import React from 'react';
import { useConst } from '@kommo-crm/react-hooks';

const Demo: React.FC = () => {
  const value = useConst<number>(() => {
    console.log("Calculating expensive value...");
    return Math.random();
  });

  return (
    <div>
      <p>Value: {value}</p>
    </div>
  );
};

Reference

const value = useConst(fn: () =>);
  • value- immutable value obtained from a function call
  • fn: () => - callback that will be called and return value

useDebounce

React hook that delays state changes until after wait milliseconds have elapsed since the last time the debounced function was invoked. The debounce delay will start when one of the values changes.

Usage

import React, { useState, ChangeEvent } from 'react';
import { useDebounce } from '@kommo-crm/react-hooks';

const Demo: React.FC = () => {
  const [searchTerm, setSearchTerm] = useState("");
  const debouncedSearchTerm = useDebounce(searchTerm, 500);

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    setSearchTerm(e.target.value);
  };

  return (
    <div>
      <h1>Debounced Search</h1>
      <input
        type="text"
        placeholder="Search..."
        value={searchTerm}
        onChange={handleChange}
      />
      <p>Searching for: {debouncedSearchTerm}</p>
    </div>
  );
};

Reference

const debouncedValue = useDebounce(value: T, delay: number);
  • debouncedValue - current delayed value
  • value: T - value, the change of which triggers the delay
  • delay: number - delay in milliseconds

useDidUpdateEffect

React hook which allows you to execute an effect only after the first render and all the next renders. This hook emulates the behavior of componentDidUpdate.

Usage

import React,{useState} from 'react';
import { useDidUpdateEffect } from '@kommo-crm/react-hooks';

const Demo: React.FC = () => {
  const [count, setCount] = useState(0);

  useDidUpdateEffect(() => {
    console.log("Effect triggered after initial render");
  }, [count]);

  return (
    <div>
      <h1>useDidUpdateEffect Example</h1>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
    </div>
  )
};

Reference

useDidUpdateEffect(fn: () =>, inputs?: React.DependencyList): void;
  • fn: () => - function that will be called
  • inputs: DependencyList - array of values that the function call depends on, in the same manner as useEffect

useKeyboardListNavigation

React hook that manages keyboard navigation for a list of items. It provides functionality to navigate up and down the list using arrow keys, select an item and toggle a state.

Usage

import React, { useState } from 'react';
import { useKeyboardListNavigation } from '@kommo-crm/react-hooks';

const Demo: React.FC = () => {
  const items = ['Item 1', 'Item 2', 'Item 3'];
  const [isOpen, setIsOpen] = useState(true);

  const { currentHoveredIndex, onKeyDown, updateListPosition } = useKeyboardListNavigation({
    itemsLength: items.length,
    isOpened: isOpen,
    hoveredIndex: 0,
    onSelect: (index) => alert(`Selected: ${items[index]}`),
    onToggle: () => setIsOpen((prev) => !prev),
  });

  return (
    <div onKeyDown={onKeyDown} tabIndex={0}>
      <h1>Keyboard Navigation</h1>
      {isOpen && (
        <ul>
          {items.map((item, index) => (
            <li
              key={item}
              style={{
                background: index === currentHoveredIndex ? 'lightblue' : 'transparent',
              }}
            >
              {item}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

Reference

const { currentHoveredIndex, onKeyDown, updateListPosition, } = useKeyboardListNavigation(
  options: UseKeyboardListNavigationOptions);
  • currentHoveredIndex: number - the index of the currently hovered item;
  • onKeyDown: (event: React.KeyboardEvent) => void - handler function for keyboard events;
  • updateHoveredIndex: (index: number) => void - Function for update hovered index;
  • updateListPosition: (index: number) => void - Function for update hovered index and list position;

useIsComponentMounted

This hook designed to be used to avoid state updates on unmounted components.

A React hook that returns a function to determine if the component is currently mounted.

Usage

import React from 'react';
import { useIsComponentMounted } from '@kommo-crm/react-hooks';

const Demo: React.FC = () => {
  const isMounted = useIsComponentMounted();

  useEffect(() => {
    setTimeout(() => {
      if (isMounted()) {
        // ...
      } else {
        // ...
      }
    }, 1000);
  }, []);
};

Reference

const isMounted = useIsComponentMounted(): (() => boolean);
  • isMounted: Function - function that will return true if component mounted and false otherwise;

useOnOutsideClick

This hook is designed to track a click outside of the passed DOM element.

Custom hook that handles clicks outside a specified element.

Usage

import React, { useRef } from 'react';
import { useOnOutsideClick } from '@kommo-crm/react-hooks';

const Demo: React.FC = () => {
  const ref = useRef<HTMLDivElement>(null);

  useOnOutsideClick({
    ref,
    handler: (event) => {
      console.log('Clicked outside the element', event);
    },
  });

  return (
    <div>
      <div ref={ref} style={{ padding: '20px', background: '#f0f0f0' }}>
        Click inside this box
      </div>
      <p>Click outside the box to trigger the handler.</p>
    </div>
  );
};

Reference

const useOnOutsideClick: ({
  ref,
  handler,
  context,
}: UseOnOutsideClickOptions) => void;
  • ref: MutableRefObject<HTMLElement> - ref DOM element, click outside of which will be tracked and processed handler
  • handler: Function - function that will be called when clicking outside the ref
  • context: string - context for grouping handlers and tracked items. If specified, handlers and links will be grouped within the specified context. Handlers of one context will not affect handlers of other contexts.
  • global - General context used by default

useDeepCompareEffect

React hook which allows you to execute an effect only when the dependencies deeply change. This hook is useful when you want to avoid unnecessary effect executions due to shallow comparison of dependencies.

Usage

import React, { useState } from 'react';
import { useDeepCompareEffect } from '@kommo-crm/react-hooks';

const Demo: React.FC = () => {
  const [data, setData] = useState({ count: 0 });

  useDeepCompareEffect(() => {
    console.log("Effect triggered due to deep change in dependencies");
  }, [data]);

  useEffect(() => {
    console.log("Effect triggered due to usual change in dependencies");
  }, [data]);

  return (
    <div>
      <h1>useDeepCompareEffect Example</h1>
      <p>Count: {data.count}</p>
      <button onClick={() => setData({ count: data.count + 1 })}>Increment Count</button>
      <button onClick={() => setData({ count: data.count })}>New object without effect</button>
    </div>
  )
};

Links