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

React Hook Portal

 import React, {useEffect, useRef} from 'react';

import ReactDOM from 'react-dom';


interface Props {

    children: React.ReactNode;

}


export function Portal (props: Props): JSX.Element {


    const container: React.MutableRefObject<HTMLDivElement> = useRef<HTMLDivElement>(document.createElement('div'));


    useEffect(function (): () => void {

        document.body.appendChild(container.current);

        const elementForRemoval: HTMLDivElement = container.current;


        return function (): void {

            document.body.removeChild(elementForRemoval);

        };

    }, [container]);


    return ReactDOM.createPortal(props.children, container.current);


}


interface DialogProps {

    open: boolean;

    children?: React.ReactNode;

}


export function Dialog (props: DialogProps): JSX.Element {

    if (props.open) {

        return (

            <Portal>

                <div>{props.children}</div>

            </Portal>

        );

    } else {

        return null;

    }

}

React Hook Disable parent DIV click

 import React, {useState} from 'react';


const [mouseButtonDownTime, setMouseButtonDownTime] = useState<Date>(null);


function handleSetMouseButtonDownTime (event: React.MouseEvent<HTMLDivElement>): void {

    if (event.button === 0) {

        setMouseButtonDownTime(new Date());

    }

}


function hasParent (child): boolean {

    let node = child.parentNode;

    while (node !== null) {

        if (node.classList && node.classList.contains('disabled-parent-click')) {

            return true;

        }

        node = node.parentNode;

    }

    return false;

}


function handleParentDivClick (message: string): (event: React.MouseEvent<HTMLDivElement>) => void {

    return function (event: React.MouseEvent<HTMLDivElement>): void {

        if (

            event.button === 0 &&

            mouseButtonDownTime !== null &&

            (new Date().getTime() - mouseButtonDownTime.getTime() < 200)

        ) {

            if (hasParent(event.target)) {

                return;

            }

            console.log(message);

        }

    };

}


function handleChildDivClick (): void {

    console.log('Child DIV click.');

}


return  (

    <div onMouseDown={handleSetMouseButtonDownTime} onMouseUp={handleParentDivClick('Parent DIV click.')}>   

        <span>Parent DIV.</span>

        <div className={'disabled-parent-click'} onClick={handleChildDivClick}>Child DIV.</div>

    </div>

);

React Hook useReduxState

 /* eslint-disable react-hooks/rules-of-hooks */


import {useSelector} from 'react-redux';


import get from 'lodash-es/get';


import {RootStateInterface} from './interfaces';


type RootStateKey = keyof RootStateInterface;

type StateKeyWithDots = `.${string}`;

export type RootOrDotsStateKey = RootStateKey | StateKeyWithDots;


interface SelectedStateInterface extends Partial<RootStateInterface> {

    [key: StateKeyWithDots]: any;

}


export function useReduxState (firstStateKey: RootOrDotsStateKey, ...stateKeys: RootOrDotsStateKey[]): SelectedStateInterface {

    stateKeys.push(firstStateKey);

    const selectedState: SelectedStateInterface = {};

    stateKeys.forEach(function (stateKey: RootOrDotsStateKey): void {

        selectedState[stateKey] = useSelector(function (state: RootStateInterface): any {

            if (stateKey.indexOf('.') === 0) {

                return get(state, stateKey.slice(1));

            }

            return state[stateKey];

        });

    });

    return selectedState;

}


    const {

       projectData: {

            value: projectDataValue

        },

        clusterSelect: {

            value: custerValue

        }

        '.buildVersion.ui.requestData': uiVersion

    } = useReduxState(

        'projectsData',

        'clusterSelect',

        'buildVersion'

    );

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

React Hook Click Outside

import React, {useEffect} from 'react';


export function useOutsideClickEventHandler <T extends HTMLElement = HTMLElement> (ref: React.RefObject<T>, handler: (event: Event) => void): void {

    useEffect(function (): () => void {

        function handleClickOutside (event: Event): void {

            if (ref.current && !ref.current.contains(event.target as Node)) {

                handler(event);

            }

        }

        document.addEventListener('mousedown', handleClickOutside);

        return function (): void {

            document.removeEventListener('mousedown', handleClickOutside);

        };

    }, [ref, handler]);

}


const elementRef: React.MutableRefObject<HTMLDivElement> = useRef<HTMLDivElement>(null);

    

useOutsideClickEventHandler(elementRef, function (): void {

    console.log('You clicked outside div.');

});


return <div ref={elementRef}>Try to click outside this div.</div>;