пятница, 14 июля 2023 г.

React Hook useState with callback

 import {

    useState, useRef, useCallback, useEffect

} from 'react';


export function useStateWithCallback <T> (initialState: T | (() => T), alwayExecuteCallback: boolean = false): [T, (newState: T, callback: (state?: T) => void) => void] {


    // Хук работает следующим образом.

    // Сначала он вызывает стандартный хук useState() до первого рендеринга для создания стартового состояния state и функции setState() для его последующего изменения.

    // Для того, чтобы сделать изменение состояния state, будет вызываться возвращаемая функция setStateCallback(), которая записывает в ref переданную функцию callback(), чтобы ее потом можно было вызвать внутри useEffect().

    // Далее вызов переданной функции callback() будет производиться внутри useEffect(), вызываемого после каждого рендеринга компонента, вызванного изменением состояния state.


    // Создание стартового состояния и функции для его последующего изменения.

    const [state, setState] = useState<T>(initialState);


    // Ссылка на функцию callback(), которая будет вызываться внутри useEffect() после изменения состояния state.

    const callbackRef: React.MutableRefObject<((state: T) => void) | undefined> = useRef<((state: T) => void) | undefined>(undefined);


    // Функция изменения состояния и установки ссылки на переданную функцию обратного вызова.

    // С помощью useCallback() выполняется мемоизация функции при первом рендеринге компонента с целью оптимизации для того, чтобы функция каждый раз не создавалась при вызове хука useStateWithCallback().

    const setStateCallback = useCallback(function setStateCallback (newState: T, callback: (state?: T) => void): void {

        callbackRef.current = callback; // Переданную функцию callback нужно передать в ref, чтобы во время выполнения useEffect() достать её оттуда.

        setState(newState); // Вызвать обновление состояния, которое затем приведет к вызову useEffect().

    }, []);


    useEffect(function (): void {

        // При первом рендере ссылка callbackRef.current равна undefined. Она становится равна функции callback() после того, как будет во внешнем коде вызвана функция setStateCallback().

        if (callbackRef.current) {

            callbackRef.current(state); // Вызываем переданную функцию callback().

            callbackRef.current = undefined; // Удаляем ссылку на функцию callback() после того, как она выполнилась, для того, чтобы она не вызывалась повторно при следующем выполнении хука useEffect().

        }

    }, alwayExecuteCallback ? undefined : [state]); // eslint-disable-line react-hooks/exhaustive-deps


    return [state, setStateCallback]; // Возвращаем из хука аналоги стандартных state и setState с возможностью вызова функции callback().


}


// Пример использования хука useStateWithCallback.

// const [value, setValue] = useStateWithCallback(1);

// setValue(2, function callback (state) {console.log(state);}); // В консоль будет выведено: 2

Комментариев нет:

Отправить комментарий