В тази статия ние се потапяме в света на персонализираните куки React и изследваме невероятния потенциал, който притежават за презареждане на вашите работни проекти. С над 20 внимателно изработени куки на ваше разположение, аз лично използвам тези куки в собствените си работни проекти и сега се вълнувам да ги споделя с вас. От подобряване на функционалността до рационализиране на работните потоци, тези персонализирани куки са предназначени да дадат възможност на разработчиците и да осигурят лесни за потребителя изживявания. Присъединете се към нас в това пътуване, докато отприщваме силата на тези 20+ кукички и отключваме нови нива на продуктивност и иновации във вашите React проекти.

Github: https://github.com/sergeyleschev/react-custom-hooks

React Hooks са функция, въведена във версия 16.8 на React, която революционизира начина, по който разработчиците пишат и управляват логиката на състоянието във функционални компоненти. Преди това логиката на състоянието можеше да бъде внедрена само в компоненти на класа, използвайки методи на жизнения цикъл. С React Hooks обаче разработчиците вече могат да използват състояние и други функции на React директно във функционални компоненти. Кукичките предоставят начин за лесно повторно използване на логиката със състояние в множество компоненти, подобрявайки повторната употреба на кода и намалявайки сложността. Те позволяват на разработчиците да разделят сложните компоненти на по-малки, по-управляеми части, което води до по-чист и по-поддържан код. Кукичките, като useState и useEffect, позволяват на разработчиците да управляват състоянието на компонентите и да се справят със страничните ефекти без усилие. Със своята простота и гъвкавост React Hooks се превърнаха в основен инструмент за изграждане на модерни, ефективни и мащабируеми React приложения.

Персонализираните кукички на React са функции за многократна употреба, които позволяват на разработчиците да абстрахират и капсулират сложна логика по начин, който може да се използва многократно. Персонализираните куки се създават чрез комбиниране на съществуващи React куки или други персонализирани куки. Те позволяват на разработчиците да извличат обща логика от компонентите и да я споделят в различни части на приложение. Персонализираните кукички следват конвенция за именуване с помощта на префикса „използване“, което им позволява да се възползват от предимствата на правилата на React за куките. Създавайки персонализирани кукички, разработчиците могат да модулират и организират своя код, което го прави по-четлив, поддържаем и тестван. Тези куки могат да капсулират всякакъв вид логика, като API извиквания, обработка на формуляри, управление на състоянието или дори абстрахиране на външни библиотеки. Персонализираните кукички на React са мощен инструмент, който насърчава повторното използване на кода и намалява дублирането, което прави разработката по-ефективна и мащабируема.

React Custom Hooks @ 2023, S. Leschev. Инженерно ниво на Google: L6+

1. useArray | sources

import { useState } from "react"

export default function useArray(defaultValue) {
    const [array, setArray] = useState(defaultValue)

    function push(element) {
        setArray(a => [...a, element])
    }

    function filter(callback) {
        setArray(a => a.filter(callback))
    }

    function update(index, newElement) {
        setArray(a => [
            ...a.slice(0, index),
            newElement,
            ...a.slice(index + 1, a.length),
        ])
    }

    function remove(index) {
        setArray(a => [...a.slice(0, index), ...a.slice(index + 1, a.length)])
    }

    function clear() {
        setArray([])
    }

    return { array, set: setArray, push, filter, update, remove, clear }
}

Куката useArray използва куката useState от React за инициализиране и управление на състоянието на масива. Той връща обект със следните функции:

  • push(element): Добавя посочения елемент към масива.
  • filter(callback): Филтрира масива въз основа на предоставената функция за обратно извикване, като премахва елементи, които не отговарят на условието.
  • update(index, newElement): Заменя елемента на посочения индекс с newElement.
  • remove(index): Премахва елемента с посочения индекс от масива.
  • clear(): Изчиства масива, като го настройва на празен масив.

Предимствата от използването на тази персонализирана кука са две: опростява управлението на състоянията на масива и осигурява по-чиста и по-четима структура на кода. С куката useArray можете лесно да добавяте, актуализирате, премахвате, филтрирате и изчиствате елементи в масив, без да се занимавате със сложна логика.

import useArray from "./useArray"

export default function ArrayComponent() {
    const { array, set, push, remove, filter, update, clear } = useArray([
        1, 2, 3, 4, 5, 6,
    ])

    return (
        <div>
            <div>{array.join(", ")}</div>
            <button onClick={() => push(7)}>Add 7</button>
            <button onClick={() => update(1, 9)}>Change Second Element To 9</button>
            <button onClick={() => remove(1)}>Remove Second Element</button>
            <button onClick={() => filter(n => n < 3)}>
                Keep Numbers Less Than 4
            </button>
            <button onClick={() => set([1, 2])}>Set To 1, 2</button>
            <button onClick={clear}>Clear</button>
        </div>
    )
}

2. useAsync | sources

import { useCallback, useEffect, useState } from "react"

export default function useAsync(callback, dependencies = []) {
    const [loading, setLoading] = useState(true)
    const [error, setError] = useState()
    const [value, setValue] = useState()

    const callbackMemoized = useCallback(() => {
        setLoading(true)
        setError(undefined)
        setValue(undefined)
        callback()
            .then(setValue)
            .catch(setError)
            .finally(() => setLoading(false))
    }, dependencies)

    useEffect(() => {
        callbackMemoized()
    }, [callbackMemoized])

    return { loading, error, value }
}

Куката useAsync приема функция за обратно извикване, която изпълнява асинхронната операция и незадължителен масив от зависимости. Той връща обект с три свойства: зареждане, грешка и стойност. Свойството за зареждане показва дали операцията се изпълнява в момента, докато свойството за грешка съдържа всички съобщения за грешка, открити по време на процеса. И накрая, свойството стойност съдържа разрешената стойност на асинхронната операция.

Едно от значителните предимства на useAsync е способността му да запомня функцията за обратно извикване с помощта на useCallback. Това гарантира, че обратното извикване се създава само когато зависимостите се променят, предотвратявайки ненужно повторно изобразяване и оптимизирайки производителността. Освен това куката използва куките useState и useEffect, за да управлява състоянието на зареждане и да извиква мемоизираната функция за обратно извикване, когато е необходимо.

UseAsync може да се използва в широк диапазон от сценарии. Независимо дали извличате данни от API, извършвате изчисления или обработвате подавания на формуляри, тази персонализирана кука опростява управлението на асинхронни операции във вашите React компоненти. Неговата гъвкавост и лекота на използване го правят ценно допълнение към всеки React проект.

Като използвате useAsync, можете да рационализирате кодовата си база, да подобрите повторното използване и да поддържате последователно и надеждно потребителско изживяване. Опитайте го в следващия си React проект и станете свидетел на силата на опростените асинхронни операции.

import useAsync from "./useAsync"

export default function AsyncComponent() {
    const { loading, error, value } = useAsync(() => {
        return new Promise((resolve, reject) => {
            const success = false
            setTimeout(() => {
                success ? resolve("Hi") : reject("Error")
            }, 1000)
        })
    })

    return (
        <div>
            <div>Loading: {loading.toString()}</div>
            <div>{error}</div>
            <div>{value}</div>
        </div>
    )
}

3. useClickOutside | sources

import useEventListener from "../useEventListener/useEventListener"

export default function useClickOutside(ref, cb) {
    useEventListener("click", e => {
        if (ref.current == null || ref.current.contains(e.target)) return
        cb(e)
    }, document)
}

Куката useClickOutside е предназначена да опрости процеса на откриване на кликвания извън определен компонент. Чрез използване на куката useEventListener, той следи за събития с щракване на ниво документ, което ви позволява да задействате функция за обратно извикване, когато се появи щракване извън препратката на предоставения компонент.

Едно от основните предимства на useClickOutside е неговата лекота на използване. Просто импортирайте куката във вашия компонент и предайте референцията на желания компонент и функцията за обратно извикване. Куката се грижи за настройката и почистването на слушателя на събития, спестявайки ви време и усилия. Плюс това, той работи безпроблемно с функционални компоненти, използвайки куките useState и useRef.

Потенциалните приложения за използване на ClickOutside са безкрайни. Това е особено полезно при внедряване на модални прозорци, падащи менюта или всеки елемент, който трябва да бъде затворен, когато потребител взаимодейства с нещо извън него. Чрез включването на useClickOutside можете да подобрите потребителското изживяване чрез предоставяне на интуитивни и ефективни взаимодействия.

За да видите useClickOutside в действие, погледнете примера по-горе. В този случай ClickOutsideComponent използва куката за превключване на видимостта на модален прозорец. Когато потребителят щракне извън модала, предоставената функция за обратно извикване задава отвореното състояние на false, затваряйки модала. По този начин компонентът предлага елегантен и удобен за потребителя начин за управление на видимостта на модала.

import { useRef, useState } from "react"
import useClickOutside from "./useClickOutside"

export default function ClickOutsideComponent() {
    const [open, setOpen] = useState(false)
    const modalRef = useRef()

    useClickOutside(modalRef, () => {
        if (open) setOpen(false)
    })

    return (
        <>
            <button onClick={() => setOpen(true)}>Open</button>
            <div
                ref={modalRef}
                style={{
                    display: open ? "block" : "none",
                    backgroundColor: "blue",
                    color: "white",
                    width: "100px",
                    height: "100px",
                    position: "absolute",
                    top: "calc(50% - 50px)",
                    left: "calc(50% - 50px)",
                }}
            >
                <span>Modal</span>
            </div>
        </>
    )
}

4. useCookie | sources

import { useState, useCallback } from "react"
import Cookies from "js-cookie"

export default function useCookie(name, defaultValue) {
    const [value, setValue] = useState(() => {
        const cookie = Cookies.get(name)
        if (cookie) return cookie
        Cookies.set(name, defaultValue)
        return defaultValue
    })

    const updateCookie = useCallback(
        (newValue, options) => {
            Cookies.set(name, newValue, options)
            setValue(newValue)
        },
        [name]
    )

    const deleteCookie = useCallback(() => {
        Cookies.remove(name)
        setValue(null)
    }, [name])

    return [value, updateCookie, deleteCookie]
}

Куката useCookie ви позволява безпроблемно да обработвате бисквитки, като предоставя кратък интерфейс. При инициализация useCookie извлича стойността на бисквитката с посоченото име. Ако бисквитката съществува, тя връща стойността си; в противен случай задава бисквитката на предоставената стойност по подразбиране. Това гарантира безпроблемно изживяване за вашите потребители, тъй като желаните данни са лесно достъпни.

Едно от ключовите предимства на тази персонализирана кука е възможността за актуализиране на стойността на бисквитката. Функцията updateCookie, върната от useCookie, ви позволява да променяте стойността на бисквитката. Чрез извикване на тази функция с нова стойност и незадължителни опции, като изтичане или път, можете незабавно да актуализирате бисквитката. Освен това, куката удобно актуализира състоянието, поддържайки приложението ви в синхрон с модифицираната бисквитка.

В сценарии, при които трябва да премахнете бисквитка, функцията deleteCookie идва на помощ. Просто извикайте тази функция и тя ще премахне посочената бисквитка от браузъра. Куката се грижи за актуализирането на състоянието, като гарантира, че вашето приложение отразява премахването на бисквитката.

Персонализираната кука useCookie е много гъвкава и може да се използва в различни контексти. Това е особено полезно при работа с потребителски предпочитания, токени за удостоверяване или всякакви данни, които трябва да се съхраняват в различни сесии. Независимо дали създавате прост формуляр за влизане, количка за пазаруване или богато на функции приложение, useCookie опростява управлението на бисквитките, спестявайки ви ценно време за разработка.

import useCookie from "./useCookie"

export default function CookieComponent() {
    const [value, update, remove] = useCookie("name", "John")

    return (
        <>
            <div>{value}</div>
            <button onClick={() => update("Sally")}>Change Name To Sally</button>
            <button onClick={remove}>Delete Name</button>
        </>
    )
}

5. useCopyToClipboard | sources

import { useState } from "react"
import copy from "copy-to-clipboard"

export default function useCopyToClipboard() {
    const [value, setValue] = useState()
    const [success, setSuccess] = useState()

    const copyToClipboard = (text, options) => {
        const result = copy(text, options)
        if (result) setValue(text)
        setSuccess(result)
    }

    return [copyToClipboard, { value, success }]
}

Копирането на текст в клипборда в React приложение може да бъде досадна задача. За да опростя този процес, създадох мощна персонализирана кука, наречена useCopyToClipboard. Само с няколко реда код тази кука рационализира функционалността за копиране в клипборда, предоставяйки на разработчиците безпроблемно решение.

Куката useCopyToClipboard използва куката useState от React, заедно с библиотеката за копиране в клипборда, за да постигне своята функционалност. Чрез извикване на тази персонализирана кука вие получавате достъп до две основни функции: copyToClipboard и придружаващите го променливи на състоянието.

Функцията copyToClipboard приема два параметъра: текстът за копиране и опции за конфигурация по избор. Той управлява процеса на копиране и съответно актуализира състоянието. При успех предоставеният текст се задава като текуща стойност и състоянието на успех се задава на true. Обратно, ако копирането е неуспешно, състоянието на успех остава невярно.

За да демонстрираме силата на useCopyToClipboard, нека разгледаме практическа реализация. Да предположим, че имате компонент, наречен CopyToClipboardComponent. Като използвате тази персонализирана кука, можете без усилие да копирате текст, като извикате функцията copyToClipboard, която приема желания текст като аргумент. Променливата за състояние на успех осигурява незабавна обратна връзка, което ви позволява да показвате подходящи съобщения или елементи на потребителския интерфейс въз основа на резултата от копирането.

Куката useCopyToClipboard е невероятно гъвкава и може да се използва в различни сценарии. Това е особено полезно в ситуации, в които се изисква копиране на текст, като URL адреси, съдържание за споделяне или генерирани от потребители данни. Независимо дали изграждате платформа за блогове, приложение за социални медии или друг проект, базиран на React, useCopyToClipboard опростява процеса на копиране на текст, подобрявайки потребителското изживяване и продуктивността.

import useCopyToClipboard from "./useCopyToClipboard"

export default function CopyToClipboardComponent() {
    const [copyToClipboard, { success }] = useCopyToClipboard()

    return (
        <>
            <button onClick={() => copyToClipboard("This was copied")}>
                {success ? "Copied" : "Copy Text"}
            </button>
            <input type="text" />
        </>
    )
}

6. useDarkMode | sources

import { useEffect } from "react"
import useMediaQuery from "../useMediaQuery/useMediaQuery"
import { useLocalStorage } from "../useStorage/useStorage"

export default function useDarkMode() {
    const [darkMode, setDarkMode] = useLocalStorage("useDarkMode")
    const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)")
    const enabled = darkMode ?? prefersDarkMode

    useEffect(() => {
        document.body.classList.toggle("dark-mode", enabled)
    }, [enabled])

    return [enabled, setDarkMode]
}

Тази персонализирана кука съчетава две други удобни куки useMediaQuery и useStorage, за да осигури безпроблемно изживяване в тъмен режим. Той автоматично открива предпочитаната от потребителя цветова схема и запазва състоянието на тъмен режим в локалното хранилище на браузъра.

Едно от основните предимства на „useDarkMode“ е неговата простота. Само с няколко реда код можете да активирате тъмен режим във вашето приложение React. Като извикате тази кука, ще получите текущото състояние на тъмен режим и функция за превключването му.

Куката „useDarkMode“ динамично актуализира основния клас на HTML, за да приложи стила „тъмен режим“, когато тъмен режим е активиран. Този подход гарантира последователност във всички компоненти без необходимост от ръчно манипулиране на класа.

body.dark-mode {
    background-color: #333;
}

Можете да използвате куката „useDarkMode“ в различни сценарии. Независимо дали създавате блог, платформа за електронна търговия или приложение с голямо съдържание, тъмният режим може да подобри потребителското изживяване, да намали напрежението на очите и да запази живота на батерията на устройството. Възможностите са безкрайни и тази персонализирана кука го прави лесен за внедряване.

За да го направя още по-лесно, включих прост примерен компонент, „DarkModeComponent“, който показва как да използвате куката „useDarkMode“. Като щракнете върху бутона „Превключване на тъмен режим“, можете незабавно да превключвате между светли и тъмни теми. Външният вид на бутона се променя динамично, отразявайки текущия режим.

import useDarkMode from "./useDarkMode"
import "./body.css"

export default function DarkModeComponent() {
    const [darkMode, setDarkMode] = useDarkMode()
    return (
        <button
            onClick={() => setDarkMode(prevDarkMode => !prevDarkMode)}
            style={{
                border: `1px solid ${darkMode ? "white" : "black"}`,
                background: "none",
                color: darkMode ? "white" : "black",
            }}
        >
            Toggle Dark Mode
        </button>
    )
}

7. useDebounce | sources

import { useEffect } from "react"
import useTimeout from "../useTimeout/useTimeout"

export default function useDebounce(callback, delay, dependencies) {
    const { reset, clear } = useTimeout(callback, delay)
    useEffect(reset, [...dependencies, reset])
    useEffect(clear, [])
}

Куката useDebounce използва вътрешно куката useTimeout, за да забави изпълнението на функция за обратно извикване, докато изтече определено забавяне. По този начин той предотвратява честите актуализации, причинени от бързи промени на входа или повтарящи се събития, което позволява по-гладко взаимодействие и намалено потребление на ресурси.

Едно от основните предимства на useDebounce е неговата простота и гъвкавост. Като опаковате функцията си за обратно извикване, продължителността на забавянето и всякакви зависимости в тази персонализирана кука, можете без усилие да приложите функция за премахване на отскок, без да претрупвате кода на вашия компонент. Куката се грижи за управлението на времето за изчакване и го изчиства, когато е необходимо, като гарантира, че обратното извикване се задейства само след определеното забавяне и с най-новите зависимости.

Къде можете да използвате useDebounce? Възможностите са безкрайни! Тази персонализирана кука е особено полезна в сценарии, при които трябва да обработвате потребителско въвеждане, като ленти за търсене или полета на формуляри, където искате да забавите изпълнението на действие, докато потребителят приключи с въвеждането или взаимодействието. Също така е полезно за оптимизиране на мрежовите заявки, като се гарантира, че заявките се изпращат само след като потребителят е спрял да въвежда или избира опции.

В примера по-горе ние демонстрираме силата на useDebounce чрез внедряване на прост компонент за брояч, наречен DebounceComponent. Всеки път, когато потребителят кликне върху бутона „Увеличаване“, състоянието на броя се актуализира. Въпреки това, вместо незабавно да предупреждаваме за стойността на броя, ние деактивираме функцията за предупреждение с помощта на useDebounce. Стойността на броя ще бъде предупредена само след закъснение от 1 секунда, ефективно предотвратявайки прекомерни предупреждения при бързо щракване върху бутона.

import { useState } from "react"
import useDebounce from "./useDebounce"

export default function DebounceComponent() {
    const [count, setCount] = useState(10)
    useDebounce(() => alert(count), 1000, [count])
    return (
        <div>
            <div>{count}</div>
            <button onClick={() => setCount(c => c + 1)}>Increment</button>
        </div>
    )
}

8. useDebugInformation | sources

import { useEffect, useRef } from "react"
import useRenderCount from "../useRenderCount/useRenderCount"

export default function useDebugInformation(componentName, props) {
    const count = useRenderCount()
    const changedProps = useRef({})
    const previousProps = useRef(props)
    const lastRenderTimestamp = useRef(Date.now())
    const propKeys = Object.keys({ ...props, ...previousProps })
    changedProps.current = propKeys.reduce((obj, key) => {
        if (props[key] === previousProps.current[key]) return obj
        return {
            ...obj,
            [key]: { previous: previousProps.current[key], current: props[key] },
        }
    }, {})
    const info = {
        count,
        changedProps: changedProps.current,
        timeSinceLastRender: Date.now() - lastRenderTimestamp.current,
        lastRenderTimestamp: lastRenderTimestamp.current,
    }
    useEffect(() => {
        previousProps.current = props
        lastRenderTimestamp.current = Date.now()
        console.log("[debug-info]", componentName, info)
    })
    return info
}

Когато става въпрос за отстраняване на грешки в компонентите на React, достъпът до подробна информация за рендери и промени в проп може да бъде изключително полезен. Това е мястото, където идва персонализираната кука useDebugInformation. Създадена от [Вашето име], тази усъвършенствана кука предоставя на разработчиците ценна информация за поведението на техните компоненти и помага да се идентифицират тесни места в производителността или неочаквани модели на изобразяване.

Едно от основните предимства на useDebugInformation е неговата простота. Като интегрирате само няколко реда код във вашия компонент, вие получавате достъп до множество данни за отстраняване на грешки. Куката проследява броя на изобразяванията, променените подпори, времето от последното изобразяване и клеймото за време на последното изобразяване. Тази изчерпателна информация ви дава възможност да анализирате поведението на компонентите по-ефективно и да вземате информирани решения, когато оптимизирате приложението си.

Куката useDebugInformation може да се приложи в различни сценарии. Например, представете си, че работите върху сложен компонент на формуляр, където определени реквизити задействат актуализации или влияят върху изобразяването. Като използвате useDebugInformation, можете лесно да наблюдавате как тези реквизити влияят върху производителността на вашия компонент и дали се появяват ненужни повторни изобразявания. Освен това, куката може да бъде безценна, когато проучвате защо конкретен компонент не се актуализира според очакванията или когато фино настройвате оптимизациите в критично за производителността приложение.

За да внедрите useDebugInformation, просто го импортирайте във вашия React компонент, заедно с всички други необходими кукички. В предоставения пример DebugInformationComponent използва куката useDebugInformation в ChildComponent. Чрез предаване на името на компонента и реквизитите към куката, вие получавате достъп до информационен обект, съдържащ всички съответни данни за отстраняване на грешки. След това този обект може да бъде показан или регистриран за допълнителен анализ.

import useDebugInformation from "./useDebugInformation"
import useToggle from "../useToggle/useToggle"
import { useState } from "react"

export default function DebugInformationComponent() {
    const [boolean, toggle] = useToggle(false)
    const [count, setCount] = useState(0)
    return (
        <>
            <ChildComponent boolean={boolean} count={count} />
            <button onClick={toggle}>Toggle</button>
            <button onClick={() => setCount(prevCount => prevCount + 1)}>
                Increment
            </button>
        </>
    )
}
function ChildComponent(props) {
    const info = useDebugInformation("ChildComponent", props)
    return (
        <>
            <div>{props.boolean.toString()}</div>
            <div>{props.count}</div>
            <div>{JSON.stringify(info, null, 2)}</div>
        </>
    )
}

9. useDeepCompareEffect | sources

import { useEffect, useRef } from "react"
import isEqual from "lodash/fp/isEqual"

export default function useDeepCompareEffect(callback, dependencies) {
    const currentDependenciesRef = useRef()
    if (!isEqual(currentDependenciesRef.current, dependencies)) {
        currentDependenciesRef.current = dependencies
    }
    useEffect(callback, [currentDependenciesRef.current])
}

Управлението на зависимости в React може да бъде предизвикателство, особено когато се работи със сложни структури от данни или вложени обекти. Това е мястото, където потребителската кука useDeepCompareEffect е полезна. Създаден, за да се справи с ограниченията на куката useEffect по подразбиране, useDeepCompareEffect гарантира, че обратното извикване на ефекта се задейства само когато зависимостите са дълбоко променени, използвайки функцията isEqual на lodash за точно сравнение.

Едно от ключовите предимства на useDeepCompareEffect е способността му да предотвратява ненужни повторни изобразявания. Чрез извършване на дълбоко сравнение между текущата и предишната зависимости, куката интелигентно определя дали ефектът трябва да бъде задействан, което води до оптимизирана производителност в сценарии, при които плитките сравнения не успяват.

Тази персонализирана кука може да бъде особено полезна, когато работите със сложни обекти на състояние, като например когато имате дълбоко вложени структури от данни или множество взаимосвързани състояния, които трябва да бъдат проследени. Тя ви позволява да дефинирате зависимости, които точно отразяват специфичните промени, които искате да проследите, като гарантира, че ефектът се изпълнява само когато е абсолютно необходимо.

Можете лесно да включите useDeepCompareEffect във вашите React компоненти, като го импортирате и използвате вместо традиционната кука useEffect. Чрез предаване на обратното извикване на ефекта и масив от зависимости можете да гарантирате, че вашият ефект работи ефективно и ефективно.

import { useEffect, useState, useRef } from "react"
import useDeepCompareEffect from "./useDeepCompareEffect"

export default function DeepCompareEffectComponent() {
    const [age, setAge] = useState(0)
    const [otherCount, setOtherCount] = useState(0)
    const useEffectCountRef = useRef()
    const useDeepCompareEffectCountRef = useRef()
    const person = { age: age, name: "Sergey" }
    useEffect(() => {
        useEffectCountRef.current.textContent =
            parseInt(useEffectCountRef.current.textContent) + 1
    }, [person])
    useDeepCompareEffect(() => {
        useDeepCompareEffectCountRef.current.textContent =
            parseInt(useDeepCompareEffectCountRef.current.textContent) + 1
    }, [person])
    return (
        <div>
            <div>
                useEffect: <span ref={useEffectCountRef}>0</span>
            </div>
            <div>
                useDeepCompareEffect: <span ref={useDeepCompareEffectCountRef}>0</span>
            </div>
            <div>Other Count: {otherCount}</div>
            <div>{JSON.stringify(person)}</div>
            <button onClick={() => setAge(currentAge => currentAge + 1)}>
                Increment Age
            </button>
            <button onClick={() => setOtherCount(count => count + 1)}>
                Increment Other Count
            </button>
        </div>
    )
}

10. useEffectOnce | sources

import { useEffect } from "react"

export default function useEffectOnce(cb) {
    useEffect(cb, [])
}

Куката useEffectOnce е предназначена да рационализира процеса на стартиране на ефекти само веднъж, когато компонент се монтира. Само с няколко реда код можете да премахнете необходимостта от ръчно указване на празен масив от зависимости ([]). Ето как работи:

Чрез капсулиране на повтарящия се модел useEffect, useEffectOnce ви позволява да се съсредоточите върху логиката в самата функция на ефекта. Това елегантно решение ви спестява многократното писане на шаблонен код и ви помага да поддържате вашите компонентни файлове чисти и кратки.

За да демонстрираме силата на useEffectOnce, нека разгледаме един практически пример:

import { useState } from "react"
import useEffectOnce from "./useEffectOnce"

export default function EffectOnceComponent() {
    const [count, setCount] = useState(0)
    useEffectOnce(() => alert("Hi"))
    return (
        <>
            <div>{count}</div>
            <button onClick={() => setCount(c => c + 1)}>Increment</button>
        </>
    )
}

В този случай, когато EffectOnceComponent се монтира, куката useEffectOnce задейства предупреждението „Hi“ точно веднъж. Това ви освобождава от ръчно управление на зависимостите на ефектите и гарантира, че вашият ефект работи ефективно.

Тази персонализирана кука е невероятно гъвкава и може да се използва в различни сценарии. Независимо дали трябва да извлечете първоначални данни, да настроите слушатели на събития или да инициализирате библиотеки на трети страни, useEffectOnce опростява процеса и насърчава по-чиста организация на кода.

11. useEventListener | sources

import { useEffect, useRef } from "react"

export default function useEventListener(
    eventType,
    callback,
    element = window
) {
    const callbackRef = useRef(callback)
    useEffect(() => {
        callbackRef.current = callback
    }, [callback])
    useEffect(() => {
        if (element == null) return
        const handler = e => callbackRef.current(e)
        element.addEventListener(eventType, handler)
        return () => element.removeEventListener(eventType, handler)
    }, [eventType, element])
}

Едно от основните предимства на useEventListener е неговата гъвкавост. Можете да посочите типа на събитието, функцията за обратно извикване и дори елемента, към който трябва да бъде прикачен слушателят на събитието. Тази гъвкавост ви позволява да приспособите обработката на събития към вашите специфични нужди, подобрявайки повторното използване на вашия код.

Куката също се възползва от куката useRef, за да поддържа стабилна препратка към функцията за обратно извикване. Това гарантира, че се използва най-актуалната версия на обратното извикване, дори ако тя се промени по време на жизнения цикъл на компонента. Това динамично поведение ви позволява да обработвате събития с точност и да реагирате на промените в състоянието на вашето приложение.

Куката useEventListener е универсален инструмент, който може да се използва в широк диапазон от сценарии. Независимо дали трябва да улавяте събития от клавиатурата, да слушате за събития на превъртане или да взаимодействате с въвеждането от потребителя, тази кука ви покрива. Неговата простота и елегантност го правят идеален избор за всеки проект на React, от малки приложения до широкомащабни корпоративни решения.

За да демонстрирате силата на useEventListener, разгледайте предоставения EventListenerComponent. Той използва куката, за да проследи последния клавиш, натиснат от потребителя. Само с няколко реда код можете без усилие да обработвате събития на натискане на клавиш и да актуализирате съответно състоянието на компонента. Този пример подчертава лекотата и ефективността на useEventListener, показвайки способността му да опрости управляваните от събития взаимодействия в приложенията на React.

import { useState } from "react"
import useEventListener from "./useEventListener"

export default function EventListenerComponent() {
    const [key, setKey] = useState("")
    useEventListener("keydown", e => {
        setKey(e.key)
    })
    return <div>Last Key: {key}</div>
}

12. useFetch | sources

import useAsync from "../useAsync/useAsync"

const DEFAULT_OPTIONS = {
    headers: { "Content-Type": "application/json" },
}
export default function useFetch(url, options = {}, dependencies = []) {
    return useAsync(() => {
        return fetch(url, { ...DEFAULT_OPTIONS, ...options }).then(res => {
            if (res.ok) return res.json()
            return res.json().then(json => Promise.reject(json))
        })
    }, dependencies)
}

Едно от ключовите предимства на useFetch е неговата простота. Като абстрахират логиката за извличане в кука за многократна употреба, разработчиците могат бързо и без усилие да правят HTTP заявки и да обработват отговори без повтарящ се шаблонен код. Само с няколко реда useFetch обработва мрежовата заявка, анализира JSON отговора и предоставя получените данни.

Куката useFetch също предлага гъвкавост чрез параметъра си с персонализирани опции. Разработчиците могат да предават допълнителни заглавки, параметри на заявка или опции за заявка, ако е необходимо, осигурявайки съвместимост с различни API. Куката следва най-добрите практики, като предоставя опции по подразбиране за задаване на заглавката Content-Type като application/json, насърчавайки чист и последователен код.

Друга забележителна характеристика на useFetch е неговата поддръжка за проследяване на зависимости. Чрез посочване на масив от зависимости, разработчиците могат да контролират кога куката задейства нова заявка. Тази функция подобрява оптимизирането на производителността, позволявайки селективни актуализации на данни въз основа на промени в масива на зависимостите.

Тази универсална кука може да се използва в множество сценарии. Например в компонент на React, който трябва да извлича и показва динамични данни, useFetch опростява процеса. Той се грижи за обработката на състоянията на зареждане и грешки, като поддържа компонента чист и фокусиран върху изобразяването на получените данни. Освен това useFetch е особено полезен в сценарии, при които извлечените данни се основават на динамични променливи или потребителски взаимодействия, както е показано в примера за FetchComponent.

import { useState } from "react"
import useFetch from "./useFetch"

export default function FetchComponent() {
    const [id, setId] = useState(1)
    const { loading, error, value } = useFetch(
        `https://jsonplaceholder.typicode.com/todos/${id}`,
        {},
        [id]
    )
    return (
        <div>
            <div>{id}</div>
            <button onClick={() => setId(currentId => currentId + 1)}>
                Increment ID
            </button>
            <div>Loading: {loading.toString()}</div>
            <div>{JSON.stringify(error, null, 2)}</div>
            <div>{JSON.stringify(value, null, 2)}</div>
        </div>
    )
}

13. useGeolocation | sources

import { useState, useEffect } from "react"

export default function useGeolocation(options) {
    const [loading, setLoading] = useState(true)
    const [error, setError] = useState()
    const [data, setData] = useState({})
    useEffect(() => {
        const successHandler = e => {
            setLoading(false)
            setError(null)
            setData(e.coords)
        }
        const errorHandler = e => {
            setError(e)
            setLoading(false)
        }
        navigator.geolocation.getCurrentPosition(
            successHandler,
            errorHandler,
            options
        )
        const id = navigator.geolocation.watchPosition(
            successHandler,
            errorHandler,
            options
        )
        return () => navigator.geolocation.clearWatch(id)
    }, [options])
    return { loading, error, data }
}

Кукичката useGeolocation използва куките useState и useEffect на React за управление на състоянието на зареждане, грешки и данни за геолокация. Необходим е незадължителен параметър „опции“, за да персонализирате поведението на геолокацията, което ви позволява да настроите фино точността и други настройки въз основа на вашите специфични нужди.

Едно от основните предимства на използването на Geolocation е неговата простота. Чрез капсулиране на сложната логика, необходима за достъп и обработка на геолокация, тази кука осигурява чисто и повторно използваемо решение. Куката автоматично управлява състоянието на зареждане, като го актуализира, когато се извличат данни за геолокация, и задава състоянието на грешка, ако възникнат проблеми по време на процеса.

Куката useGeolocation също така включва метода watchPosition от API за геолокация, който позволява непрекъснат мониторинг на позицията на потребителя. Това може да бъде полезно в сценарии, при които се изискват актуализации в реално време на местоположението на потребителя, като например в приложения за проследяване или интерактивни карти.

За да използвате тази кука, просто импортирайте useGeolocation във вашия компонент и деструктурирайте променливите за зареждане, грешка и данни. Обектът с данни съдържа стойностите за географска ширина и дължина, което ви позволява да показвате местоположението на потребителя във вашия потребителски интерфейс без усилие. Променливата за зареждане ви информира за текущото състояние на извличане на геолокация, а променливата за грешка предоставя всички съобщения за грешка, ако е приложимо.

Показаният по-горе GeolocationComponent демонстрира основно изпълнение на useGeolocation. Той изобразява състоянието на зареждане, съобщението за грешка (ако има такова) и стойностите на географската ширина и дължина на потребителя. Само с няколко реда код можете безпроблемно да интегрирате функционалност за геолокация във вашите React приложения.

import useGeolocation from "./useGeolocation"

export default function GeolocationComponent() {
    const {
        loading,
        error,
        data: { latitude, longitude },
    } = useGeolocation()
    return (
        <>
            <div>Loading: {loading.toString()}</div>
            <div>Error: {error?.message}</div>
            <div>
                {latitude} x {longitude}
            </div>
        </>
    )
}

14. useHover | sources

import { useState } from "react"
import useEventListener from "../useEventListener/useEventListener"

export default function useHover(ref) {
    const [hovered, setHovered] = useState(false)
    useEventListener("mouseover", () => setHovered(true), ref.current)
    useEventListener("mouseout", () => setHovered(false), ref.current)
    return hovered
}

Тази лека кука използва куките useState и useEventListener от React, за да следи състоянието на задържане. Като просто подадете реф към куката useHover, можете да започнете да получавате точни събития при задържане. Куката слуша за събития „mouseover“ и „mouseout“, актуализирайки съответно състоянието на задържане.

Едно от ключовите предимства на useHover е неговата простота и многократна употреба. Чрез капсулиране на логиката на задържане в куката, можете лесно да я използвате в множество компоненти, без да дублирате код. Това насърчава чист и поддържаем код, спестявайки ви време и усилия в дългосрочен план.

UseHover може да се използва в различни сценарии. Независимо дали трябва да маркирате елемент при задържане на мишката, да задействате допълнителни действия или динамично да променяте стилове, тази персонализирана кука ви покрива. Той осигурява безпроблемен начин за подобряване на интерактивността и потребителското изживяване на вашите React компоненти.

За да демонстрирате силата му, разгледайте примера на HoverComponent по-горе. Чрез прилагане на куката useHover към elementRef, цветът на фона на div динамично се променя между синьо и червено в зависимост от състоянието на задържане. Тази проста, но ефективна реализация демонстрира потенциала на useHover при създаването на интерактивни и ангажиращи UI компоненти.

import { useRef } from "react"
import useHover from "./useHover"

export default function HoverComponent() {
    const elementRef = useRef()
    const hovered = useHover(elementRef)
    return (
        <div
            ref={elementRef}
            style={{
                backgroundColor: hovered ? "blue" : "red",
                width: "100px",
                height: "100px",
                position: "absolute",
                top: "calc(50% - 50px)",
                left: "calc(50% - 50px)",
            }}
        />
    )
}

15. useLongPress | sources

import useEventListener from "../useEventListener/useEventListener"
import useTimeout from "../useTimeout/useTimeout"
import useEffectOnce from "../useEffectOnce/useEffectOnce"

export default function useLongPress(ref, cb, { delay = 250 } = {}) {
    const { reset, clear } = useTimeout(cb, delay)
    useEffectOnce(clear)
    useEventListener("mousedown", reset, ref.current)
    useEventListener("touchstart", reset, ref.current)
    useEventListener("mouseup", clear, ref.current)
    useEventListener("mouseleave", clear, ref.current)
    useEventListener("touchend", clear, ref.current)
}

Едно от основните предимства на useLongPress е неговата простота. Използвайки тази кука, разработчиците могат лесно да дефинират действие с продължително натискане върху всеки елемент в тяхното приложение React. Само с няколко реда код, куката се грижи за обработката на тънкостите на проследяване на продължителността на дълго натискане и задействане на свързаната функция за обратно извикване.

Куката useLongPress предлага гъвкавост чрез персонализирани опции. Разработчиците могат да определят желаното забавяне за дълго натискане, което им позволява да настроят фино продължителността, необходима за задействане на действие. Освен това кукичката се интегрира интелигентно с други персонализирани кукички като useTimeout, useEventListener и useEffectOnce, подобрявайки повторното използване на кода и поддръжката.

Приложенията за използване на LongPress са широки. Независимо дали разработвате чувствителен на допир потребителски интерфейс, прилагате контекстни менюта или създавате персонализирани жестове, тази кука се оказва ценен инструмент. От мобилни приложения до сложни уеб интерфейси, useLongPress предоставя елегантно решение за включване на взаимодействия с продължително натискане, които повишават ангажираността на потребителите и подобряват общата използваемост.

import { useRef } from "react"
import useLongPress from "./useLongPress"

export default function LongPressComponent() {
    const elementRef = useRef()
    useLongPress(elementRef, () => alert("Long Press"))
    return (
        <div
            ref={elementRef}
            style={{
                backgroundColor: "red",
                width: "100px",
                height: "100px",
                position: "absolute",
                top: "calc(50% - 50px)",
                left: "calc(50% - 50px)",
            }}
        />
    )
}

16. useMediaQuery | sources

import { useState, useEffect } from "react"
import useEventListener from "../useEventListener/useEventListener"

export default function useMediaQuery(mediaQuery) {
    const [isMatch, setIsMatch] = useState(false)
    const [mediaQueryList, setMediaQueryList] = useState(null)
    useEffect(() => {
        const list = window.matchMedia(mediaQuery)
        setMediaQueryList(list)
        setIsMatch(list.matches)
    }, [mediaQuery])
    useEventListener("change", e => setIsMatch(e.matches), mediaQueryList)
    return isMatch
}

Куката useMediaQuery ви позволява да актуализирате динамично потребителския си интерфейс въз основа на дадена медийна заявка. Просто предайте желаната медийна заявка като параметър и куката ще върне булева стойност, показваща дали медийната заявка съответства на текущия размер на прозореца за изглед.

Едно от основните предимства на тази персонализирана кука е нейната простота и възможност за многократна употреба. Само с няколко реда код можете без усилие да внедрите отзивчиво поведение във вашето приложение. Независимо дали трябва да рендирате условно компоненти, да приложите специфични стилове или да задействате различни функции въз основа на размера на екрана, useMediaQuery ще ви покрие.

Тази кука не е ограничена до конкретни случаи на употреба; може да се използва в различни сценарии. Например, можете да го използвате, за да коригирате динамично оформлението на навигационно меню, да скриете или покажете определени елементи въз основа на размера на екрана или дори да оптимизирате зареждането на данни въз основа на наличното пространство. Възможностите са безкрайни, а куката useMediaQuery ви дава възможност да предоставите безпроблемно потребителско изживяване на различни устройства и размери на екрана.

import useMediaQuery from "./useMediaQuery"

export default function MediaQueryComponent() {
    const isLarge = useMediaQuery("(min-width: 200px)")
    return <div>Large: {isLarge.toString()}</div>
}

17. useOnlineStatus | sources

import { useState } from "react"
import useEventListener from "../useEventListener/useEventListener"

export default function useOnlineStatus() {
    const [online, setOnline] = useState(navigator.onLine)
    useEventListener("online", () => setOnline(navigator.onLine))
    useEventListener("offline", () => setOnline(navigator.onLine))
    return online
}

Едно от основните предимства на “useOnlineStatus” е неговата простота. Като импортирате и използвате тази кука във вашия компонент, можете без усилие да получите достъп до онлайн състоянието на потребителя. Куката вътрешно използва свойството „navigator.onLine“, за да определи първоначалния онлайн статус и динамично го актуализира, когато връзката на потребителя се промени.

За да използвате тази кука, всичко, което трябва да направите, е да я извикате във вашия функционален компонент, точно както демонстрира примерът „OnlineStatusComponent“. Връща булева стойност, показваща дали потребителят в момента е онлайн или офлайн. След това можете да използвате тази информация, за да предоставите обратна връзка в реално време на вашите потребители или да вземете решения въз основа на техния онлайн статус.

Куката „useOnlineStatus“ може да намери приложения в широк диапазон от сценарии. Например, можете да подобрите потребителското изживяване, като покажете визуален индикатор, когато потребителят загуби връзката си с интернет, което му позволява да предприеме подходящи действия. Освен това можете условно да визуализирате определени компоненти или да задействате специфични поведения въз основа на онлайн статуса на потребителя. Възможностите са безкрайни и тази кука отваря нови възможности за изграждане на стабилни и отзивчиви React приложения.

import useOnlineStatus from "./useOnlineStatus"

export default function OnlineStatusComponent() {
    const online = useOnlineStatus()
    return <div>{online.toString()}</div>
}

18. useOnScreen | sources

import { useEffect, useState } from "react"

export default function useOnScreen(ref, rootMargin = "0px") {
    const [isVisible, setIsVisible] = useState(false)
    useEffect(() => {
        if (ref.current == null) return
        const observer = new IntersectionObserver(
            ([entry]) => setIsVisible(entry.isIntersecting),
            { rootMargin }
        )
        observer.observe(ref.current)
        return () => {
            if (ref.current == null) return
            observer.unobserve(ref.current)
        }
    }, [ref.current, rootMargin])
    return isVisible
}

Куката useOnScreen използва силата на API на Intersection Observer, което го прави ефективен и надежден. Чрез просто предоставяне на препратка към елемента, който искате да наблюдавате, useOnScreen ще ви уведоми, когато влезе или излезе от прозореца за изглед.

Едно от основните предимства на useOnScreen е неговата простота. Само с няколко реда код можете да откриете дали даден елемент е видим и да реагирате съответно. Това може да бъде изключително полезно в сценарии, при които искате да задействате анимации, лениво зареждане на изображения или зареждане на допълнително съдържание, докато потребителят превърта.

За да използвате тази кука, първо я импортирайте във вашия компонентен файл. След това създайте реф с помощта на куката useRef, за да насочите желания елемент. Предайте ref като първи аргумент на куката useOnScreen и сте готови! Можете също така да предоставите незадължителна стойност на rootMargin, за да коригирате видимия праг.

В нашия примерен код OnScreenComponentComponent демонстрира как да използвате куката useOnScreen. Като прикрепим ref към втория елемент на заглавката, можем да покажем текст „(Видим)“, когато влезе в прозореца за изглед. Чувствайте се свободни да персонализирате логиката във вашия компонент, за да отговаря на вашите специфични нужди.

import { useRef } from "react"
import useOnScreen from "./useOnScreen"

export default function OnScreenComponentComponent() {
    const headerTwoRef = useRef()
    const visible = useOnScreen(headerTwoRef, "-100px")
    return (
        <div>
            <h1>Header</h1>
            <div>
              ...
            </div>
            <h1 ref={headerTwoRef}>Header 2 {visible && "(Visible)"}</h1>
            <div>
              ...
            </div>
        </div>
    )
}

19. usePrevious | sources

import { useRef } from "react"

export default function usePrevious(value) {
    const currentRef = useRef(value)
    const previousRef = useRef()
    if (currentRef.current !== value) {
        previousRef.current = currentRef.current
        currentRef.current = value
    }
    return previousRef.current
}

Предимствата от използването на usePrevious са забележителни. Използвайки useRef, тази кука ефективно съхранява текущите и предишните стойности, като ги актуализира всеки път, когато стойността се промени. Чрез сравняване на текущите и предишните стойности можете лесно да откриете и да отговорите на промените в данните на вашия компонент.

Тази персонализирана кука може да промени играта в различни сценарии. Например, можете да използвате usePrevious, за да сравните и визуализирате промените в данните, да проследите преходите на състоянието или да приложите функционалност за отмяна/възвръщане. Освен това може да бъде ценно при обработка на формуляри, анимации и всяка ситуация, при която достъпът до предишната стойност е от решаващо значение за логиката на вашето приложение.

Нека хвърлим един поглед на това как usePrevious може да се използва на практика. Помислете за компонент на React, наречен PreviousComponent, където имаме състояние на броя, състояние на име и бутон за увеличаване на броя и промяна на името. Чрез включването на usePrevious можем без усилие да покажем текущия брой заедно с предишната му стойност, позволявайки на потребителите да визуализират промените на броя с един поглед.

import { useState } from "react"
import usePrevious from "./usePrevious"

export default function PreviousComponent() {
    const [count, setCount] = useState(0)
    const [name, setName] = useState("Sergey")
    const previousCount = usePrevious(count)
    return (
        <div>
            <div>
                {count} - {previousCount}
            </div>
            <div>{name}</div>
            <button onClick={() => setCount(currentCount => currentCount + 1)}>
                Increment
            </button>
            <button onClick={() => setName("John")}>Change Name</button>
        </div>
    )
}

20. useRenderCount | sources

import { useEffect, useRef } from "react"

export default function useRenderCount() {
    const count = useRef(1)
    useEffect(() => count.current++)
    return count.current
}

Кукичката useRenderCount използва кукичките useEffect и useRef на React, за да поддържа броя на изобразяванията. С всяко изобразяване броят се увеличава, като ви предоставя обратна връзка в реално време за честотата на изобразяване на компонента.

Едно от основните предимства на използването на useRenderCount е неговата простота. Като абстрахирате логиката в кука за многократна употреба, можете лесно да я интегрирате във всеки компонент, без да претрупвате кодовата си база. Освен това, той предоставя ясен и кратък начин за наблюдение на поведението при изобразяване, което може да бъде от решаващо значение за оптимизиране на производителността и отстраняване на грешки.

Тази универсална кука може да се прилага в различни сценарии. Например, когато разработвате сложен компонент, който показва неочаквани модели на изобразяване, useRenderCount ви помага да определите проблема, като показва точния брой изобразявания. Също така е удобен за измерване на въздействието на определени оптимизации или техники за рефакторинг, което ви позволява да оцените тяхната ефективност.

За да започнете, просто импортирайте куката useRenderCount и я извикайте във вашия компонент. Можете да видите силата му в действие, като разгледате примера за RenderCountComponent по-горе. Чрез комбиниране на useRenderCount с други персонализирани кукички като useToggle, можете да създавате интерактивни компоненти, като същевременно следите броя на изобразяванията.

import useRenderCount from "./useRenderCount"
import useToggle from "../useToggle/useToggle"

export default function RenderCountComponent() {
    const [boolean, toggle] = useToggle(false)
    const renderCount = useRenderCount()
    return (
        <>
            <div>{boolean.toString()}</div>
            <div>{renderCount}</div>
            <button onClick={toggle}>Toggle</button>
        </>
    )
}

21. useScript | sources

import useAsync from "../useAsync/useAsync"

export default function useScript(url) {
    return useAsync(() => {
        const script = document.createElement("script")
        script.src = url
        script.async = true
        return new Promise((resolve, reject) => {
            script.addEventListener("load", resolve)
            script.addEventListener("error", reject)
            document.body.appendChild(script)
        })
    }, [url])
}

Едно от значителните предимства на useScript е способността му да обработва асинхронно зареждане на скриптове. Като зададете атрибута async на скрипта на true, вие гарантирате, че той няма да блокира изобразяването на вашето приложение. Това подобрява производителността и цялостното потребителско изживяване, особено когато се работи с по-големи скриптове или бавни мрежови връзки.

UseScript може да се използва в различни сценарии. Например, можете да заредите външни библиотеки като jQuery, което ви позволява да впрегнете неговите мощни функционалности, без да добавяте обем към вашия пакет. Освен това можете да заредите аналитични скриптове, уиджети за социални медии или всеки друг скрипт, необходим за динамичното поведение на вашето приложение.

В примера по-горе виждаме как useScript се използва в ScriptComponent. Кукичката useScript се извиква с URL адреса на библиотеката jQuery като аргумент. Куката връща състоянията на зареждане и грешка, които могат да се използват за показване на зареждащ се спинер или съответно съобщение за грешка. След като скриптът се зареди успешно, компонентът показва текущата ширина на прозореца с помощта на jQuery.

import useScript from "./useScript"

export default function ScriptComponent() {
    const { loading, error } = useScript(
        "https://code.jquery.com/jquery-3.6.0.min.js"
    )
    if (loading) return <div>Loading</div>
    if (error) return <div>Error</div>
    return <div>{window.$(window).width()}</div>
}

22. useStateWithHistory | sources

import { useCallback, useRef, useState } from "react"

export default function useStateWithHistory(
    defaultValue,
    { capacity = 10 } = {}
) {
    const [value, setValue] = useState(defaultValue)
    const historyRef = useRef([value])
    const pointerRef = useRef(0)
    const set = useCallback(
        v => {
            const resolvedValue = typeof v === "function" ? v(value) : v
            if (historyRef.current[pointerRef.current] !== resolvedValue) {
                if (pointerRef.current < historyRef.current.length - 1) {
                    historyRef.current.splice(pointerRef.current + 1)
                }
                historyRef.current.push(resolvedValue)
                while (historyRef.current.length > capacity) {
                    historyRef.current.shift()
                }
                pointerRef.current = historyRef.current.length - 1
            }
            setValue(resolvedValue)
        },
        [capacity, value]
    )
    const back = useCallback(() => {
        if (pointerRef.current <= 0) return
        pointerRef.current--
        setValue(historyRef.current[pointerRef.current])
    }, [])
    const forward = useCallback(() => {
        if (pointerRef.current >= historyRef.current.length - 1) return
        pointerRef.current++
        setValue(historyRef.current[pointerRef.current])
    }, [])
    const go = useCallback(index => {
        if (index < 0 || index > historyRef.current.length - 1) return
        pointerRef.current = index
        setValue(historyRef.current[pointerRef.current])
    }, [])
    return [
        value,
        set,
        {
            history: historyRef.current,
            pointer: pointerRef.current,
            back,
            forward,
            go,
        },
    ]
}

Предимства на useStateWithHistory:

  1. Автоматично проследяване на историята: useStateWithHistory автоматично следи стойностите, които сте задали, което ви позволява да имате достъп до пълната история, когато имате нужда от нея.
  2. Ефективно използване на паметта: Куката използва параметър за капацитет, като гарантира, че историята няма да расте безкрайно. Можете да определите максималния брой исторически стойности, които да се съхраняват, предотвратявайки прекомерната консумация на памет.
  3. Функционалност за пътуване във времето: С функциите back(), forward() и go() можете безпроблемно да навигирате през записаната история. Пътувайте напред и назад между предишни състояния или прескачайте директно към конкретен индекс, като позволявате мощна функция за отмяна/възвръщане или стъпка по стъпка.

Къде да използвате useStateWithHistory:

  1. Управление на формуляри: Опростете процеса на обработка на въвеждане на формуляри, като предоставите лесен начин за проследяване на промените, връщане към предишни стойности или възстановяване на модификации.
  2. Функция за отмяна/възстановяване: Внедрете с лекота функцията за отмяна/повтаряне във вашето приложение. Проследявайте промените в състоянието и позволявайте на потребителите да навигират напред и назад през своите действия без усилие.
  3. Навигация стъпка по стъпка: Използвайте useStateWithHistory за изграждане на интерактивни ръководства или уроци, където потребителите могат да навигират между различни стъпки, като същевременно запазват напредъка си.
import { useState } from "react"
import useStateWithHistory from "./useStateWithHistory"

export default function StateWithHistoryComponent() {
    const [count, setCount, { history, pointer, back, forward, go }] =
        useStateWithHistory(1)
    const [name, setName] = useState("Sergey")
    return (
        <div>
            <div>{count}</div>
            <div>{history.join(", ")}</div>
            <div>Pointer - {pointer}</div>
            <div>{name}</div>
            <button onClick={() => setCount(currentCount => currentCount * 2)}>
                Double
            </button>
            <button onClick={() => setCount(currentCount => currentCount + 1)}>
                Increment
            </button>
            <button onClick={back}>Back</button>
            <button onClick={forward}>Forward</button>
            <button onClick={() => go(2)}>Go To Index 2</button>
            <button onClick={() => setName("John")}>Change Name</button>
        </div>
    )
}

23. useStateWithValidation | sources

import { useState, useCallback } from "react"

export default function useStateWithValidation(validationFunc, initialValue) {
    const [state, setState] = useState(initialValue)
    const [isValid, setIsValid] = useState(() => validationFunc(state))
    const onChange = useCallback(
        nextState => {
            const value =
                typeof nextState === "function" ? nextState(state) : nextState
            setState(value)
            setIsValid(validationFunc(value))
        },
        [validationFunc]
    )
    return [state, onChange, isValid]
}

Кукичката useStateWithValidation съчетава куките useState и useCallback от React, за да осигури елегантно решение. Необходими са два параметъра: функция за валидиране и начална стойност. Функцията за валидиране определя дали текущото състояние се счита за валидно или не.

Едно от основните предимства на тази персонализирана кука е нейната гъвкавост. Можете да преминете всяка функция за валидиране, която отговаря на вашите специфични изисквания. Независимо дали става въпрос за проверка на дължината на низ, гарантиране, че числова стойност попада в определен диапазон или извършване на по-сложни проверки, useStateWithValidation ви покрива.

import useStateWithValidation from "./useStateWithValidation"

export default function StateWithValidationComponent() {
    const [username, setUsername, isValid] = useStateWithValidation(
        name => name.length > 5,
        ""
    )
    return (
        <>
            <div>Valid: {isValid.toString()}</div>
            <input
                type="text"
                value={username}
                onChange={e => setUsername(e.target.value)}
            />
        </>
    )
}

В този пример StateWithValidationComponent използва куката useStateWithValidation, за да управлява състоянието на потребителското име. Функцията за валидиране проверява дали дължината на потребителското име е по-голяма от 5 знака, а променливата isValid отразява валидността на текущия вход.

24. useStorage | sources

import { useCallback, useState, useEffect } from "react"

export function useLocalStorage(key, defaultValue) {
    return useStorage(key, defaultValue, window.localStorage)
}
export function useSessionStorage(key, defaultValue) {
    return useStorage(key, defaultValue, window.sessionStorage)
}
function useStorage(key, defaultValue, storageObject) {
    const [value, setValue] = useState(() => {
        const jsonValue = storageObject.getItem(key)
        if (jsonValue != null) return JSON.parse(jsonValue)
        if (typeof defaultValue === "function") {
            return defaultValue()
        } else {
            return defaultValue
        }
    })
    useEffect(() => {
        if (value === undefined) return storageObject.removeItem(key)
        storageObject.setItem(key, JSON.stringify(value))
    }, [key, value, storageObject])
    const remove = useCallback(() => {
        setValue(undefined)
    }, [])
    return [value, setValue, remove]
}

Куката useStorage осигурява две удобни функции: useLocalStorage и useSessionStorage. С useLocalStorage можете без усилие да съхранявате и извличате данни в локалното хранилище на браузъра, докато useSessionStorage предлага същата функционалност, но вместо това с хранилището на сесията.

Едно от основните предимства на тази персонализирана кука е нейната простота. Можете да го използвате за съхраняване на всякакъв тип данни, като низове, числа или дори сложни обекти, само с няколко реда код. Освен това useStorage обработва сериализацията и десериализацията на данни вместо вас, така че не е нужно да се притеснявате за преобразуването на стойности към и от JSON.

Друго предимство е автоматичната синхронизация между съхранените данни и състоянието на компонента. Всеки път, когато съхранените данни се променят, куката актуализира съответно състоянието на компонента. По същия начин, когато състоянието на компонента се промени, куката автоматично запазва новата стойност в хранилището. Тази двупосочна синхронизация гарантира, че вашето приложение винаги отразява най-новите данни, което го прави идеално за сценарии, при които актуализациите в реално време са от решаващо значение.

Куката useStorage предоставя също функция за премахване, която ви позволява лесно да изтриете съхранени стойности, когато вече не са необходими. Тази функционалност е полезна при прилагане на функции като бутони за излизане или изчистване на специфични за потребителя данни.

Можете да използвате куката useStorage в различни сценарии. Например, представете си, че имате панел с настройки, където потребителите могат да персонализират своите предпочитания. С помощта на useLocalStorage можете лесно да съхранявате и извличате тези настройки, като гарантирате, че те ще се запазят при презареждане на страницата или дори ако потребителят затвори и отвори отново браузъра.

import { useSessionStorage, useLocalStorage } from "./useStorage"

export default function StorageComponent() {
    const [name, setName, removeName] = useSessionStorage("name", "Sergey")
    const [age, setAge, removeAge] = useLocalStorage("age", 26)
    return (
        <div>
            <div>
                {name} - {age}
            </div>
            <button onClick={() => setName("John")}>Set Name</button>
            <button onClick={() => setAge(40)}>Set Age</button>
            <button onClick={removeName}>Remove Name</button>
            <button onClick={removeAge}>Remove Age</button>
        </div>
    )
}

25. useTimeout | sources

import { useCallback, useEffect, useRef } from "react"

export default function useTimeout(callback, delay) {
    const callbackRef = useRef(callback)
    const timeoutRef = useRef()
    useEffect(() => {
        callbackRef.current = callback
    }, [callback])
    const set = useCallback(() => {
        timeoutRef.current = setTimeout(() => callbackRef.current(), delay)
    }, [delay])
    const clear = useCallback(() => {
        timeoutRef.current && clearTimeout(timeoutRef.current)
    }, [])
    useEffect(() => {
        set()
        return clear
    }, [delay, set, clear])
    const reset = useCallback(() => {
        clear()
        set()
    }, [clear, set])
    return { reset, clear }
}

Куката „useTimeout“ капсулира логиката за настройка, изчистване и нулиране на таймаути в компонент на React. Необходими са два параметъра: функция за обратно извикване и продължителност на забавяне в милисекунди. Всеки път, когато определеното забавяне изтече, предоставената функция за обратно извикване се изпълнява.

Едно от значителните предимства на тази персонализирана кука е, че тя гарантира, че функцията за обратно извикване остава актуална, дори ако се промени по време на повторно изобразяване на компонент. Чрез използване на useRef за съхраняване на препратката за обратно извикване, куката гарантира, че винаги се извиква най-новата версия на функцията.

Освен това куката „useTimeout“ оптимизира производителността чрез използване на useCallback за запомняне на функциите „set“ и „clear“. Това означава, че функциите се пресъздават само когато техните зависимости се променят, предотвратявайки ненужни рендери и повишавайки ефективността.

Куката „useTimeout“ може да се използва в различни сценарии, където се изискват действия във времето. Например, в компонент за обратно броене като „TimeoutComponent“, показан по-горе, можете лесно да внедрите таймер, който се нулира след определена продължителност. С помощта на куката „useTimeout“ можете без усилие да актуализирате стойността на обратното броене и да управлявате времето за изчакване, без да се притеснявате за сложен код за управление на изчакването.

import { useState } from "react"
import useTimeout from "./useTimeout"

export default function TimeoutComponent() {
    const [count, setCount] = useState(10)
    const { clear, reset } = useTimeout(() => setCount(0), 1000)
    return (
        <div>
            <div>{count}</div>
            <button onClick={() => setCount(c => c + 1)}>Increment</button>
            <button onClick={clear}>Clear Timeout</button>
            <button onClick={reset}>Reset Timeout</button>
        </div>
    )
}

26. useToggle | sources

import { useState } from "react"

export default function useToggle(defaultValue) {
    const [value, setValue] = useState(defaultValue)
    function toggleValue(value) {
        setValue(currentValue =>
            typeof value === "boolean" ? value : !currentValue
        )
    }
    return [value, toggleValue]
}

Едно от основните предимства на useToggle е неговата гъвкавост. С един ред код можете да инициализирате състоянието със стойност по подразбиране. Функцията toggleValue ви позволява лесно да превключвате състоянието между true и false или можете да предадете директно булева стойност, за да зададете състоянието на желаната от вас стойност. Тази гъвкавост прави useToggle идеален за широк набор от сценарии, при които се изисква превключване или превключване на състояние.

UseToggle може да се интегрира безпроблемно в различни компоненти на React. Например в предоставения ToggleComponent куката useToggle се използва за управление на състоянието на бутон за превключване. С едно просто щракване състоянието на бутона се превключва между true и false. Освен това куката предоставя бутони за директно задаване на стойността на true или false, като се грижи за конкретни случаи на употреба. Полученото състояние се показва динамично, което позволява незабавна обратна връзка.

import useToggle from "./useToggle"

export default function ToggleComponent() {
    const [value, toggleValue] = useToggle(false)
    return (
        <div>
            <div>{value.toString()}</div>
            <button onClick={toggleValue}>Toggle</button>
            <button onClick={() => toggleValue(true)}>Make True</button>
            <button onClick={() => toggleValue(false)}>Make False</button>
        </div>
    )
}

27. useTranslation | sources

import { useLocalStorage } from "../useStorage/useStorage"
import * as translations from "./translations"

export default function useTranslation() {
    const [language, setLanguage] = useLocalStorage("language", "en")
    const [fallbackLanguage, setFallbackLanguage] = useLocalStorage(
        "fallbackLanguage",
        "en"
    )
    const translate = key => {
        const keys = key.split(".")
        return (
            getNestedTranslation(language, keys) ??
            getNestedTranslation(fallbackLanguage, keys) ??
            key
        )
    }
    return {
        language,
        setLanguage,
        fallbackLanguage,
        setFallbackLanguage,
        t: translate,
    }
}
function getNestedTranslation(language, keys) {
    return keys.reduce((obj, key) => {
        return obj?.[key]
    }, translations[language])
}

Едно от ключовите предимства на useTranslation е неговата безпроблемна интеграция с localStorage на браузъра. Той автоматично запазва избрания език и предпочитанията за резервен език, така че вашите потребители ще виждат съдържанието на предпочитания от тях език всеки път, когато посетят приложението ви.

Куката използва куката useLocalStorage от библиотеката useStorage, за да запази езиковите настройки. Това гарантира, че дори ако потребителят обнови страницата или навигира и се върне обратно, техните езикови предпочитания ще бъдат запазени.

Използването на useTranslation е невероятно лесно. Просто импортирайте куката и я инициализирайте във вашия компонент. Ще имате достъп до текущия език, възможността да зададете езика, резервния език и опцията да зададете резервния език. Освен това куката предоставя удобна функция за превод, t, която приема ключ като вход и връща съответната преведена стойност.

Можете да използвате куката useTranslation в различни сценарии. Независимо дали изграждате многоезичен уебсайт, интернационализирано приложение или просто трябва да поддържате преводи във вашите UI компоненти, тази кука ще опрости процеса и ще направи кодовата ви база по-лесна за поддръжка.

import useTranslation from "./useTranslation"

export default function TranslationComponent() {
    const { language, setLanguage, setFallbackLanguage, t } = useTranslation()
    return (
        <>
            <div>{language}</div>
            <div>{t("hi")}</div>
            <div>{t("bye")}</div>
            <div>{t("nested.value")}</div>
            <button onClick={() => setLanguage("sp")}>Change To Spanish</button>
            <button onClick={() => setLanguage("en")}>Change To English</button>
            <button onClick={() => setFallbackLanguage("sp")}>Change FB Lang</button>
        </>
    )
}

28. useUpdateEffect | sources

import { useEffect, useRef } from "react"

export default function useUpdateEffect(callback, dependencies) {
    const firstRenderRef = useRef(true)
    useEffect(() => {
        if (firstRenderRef.current) {
            firstRenderRef.current = false
            return
        }
        return callback()
    }, dependencies)
}

Куката useUpdateEffect е проектирана да изпълнява функция за обратно извикване само след първоначалното изобразяване. Това поведение е особено полезно, когато искате да извършвате действия въз основа на промени в състоянието, като пропускате първоначалното изпълнение. Използвайки куката useRef, useUpdateEffect проследява първото изобразяване и пропуска обратното извикване по време на тази фаза.

Едно от ключовите предимства на useUpdateEffect е неговата простота. Само с няколко реда код можете да подобрите своите компоненти на React чрез ефективно боравене с актуализации на състоянието. Като посочите зависимостите за куката, можете да контролирате точно кога трябва да се задейства обратното извикване, предотвратявайки ненужни цикли на изобразяване.

Тази персонализирана кука може да се използва в различни сценарии. Например, представете си, че имате компонент за брояч, който трябва да показва предупреждение всеки път, когато броячът се промени, с изключение на първоначалното изобразяване. С помощта на useUpdateEffect можете лесно да постигнете това поведение, като подобрите потребителското изживяване и намалите ненужните сигнали.

За да приложите useUpdateEffect, просто го импортирайте във вашия React компонент и дефинирайте функцията за обратно извикване и зависимостите. Куката ще се погрижи за останалото, като гарантира, че обратното извикване се изпълнява само когато е необходимо. Това е мощен инструмент, който опростява управлението на състоянието и подобрява производителността на вашите React приложения.

import { useState } from "react"
import useUpdateEffect from "./useUpdateEffect"

export default function UpdateEffectComponent() {
    const [count, setCount] = useState(10)
    useUpdateEffect(() => alert(count), [count])
    return (
        <div>
            <div>{count}</div>
            <button onClick={() => setCount(c => c + 1)}>Increment</button>
        </div>
    )
}

29. useWindowSize | sources

import { useState } from "react"
import useEventListener from "../useEventListener/useEventListener"

export default function useWindowSize() {
    const [windowSize, setWindowSize] = useState({
        width: window.innerWidth,
        height: window.innerHeight,
    })
    useEventListener("resize", () => {
        setWindowSize({ width: window.innerWidth, height: window.innerHeight })
    })
    return windowSize
}

Едно от основните предимства на useWindowSize е неговата лекота на използване. Като просто импортирате куката и я извиквате във вашия функционален компонент, вие получавате достъп до обект, съдържащ текущата ширина и височина на прозореца. Това елиминира необходимостта от шаблонен код и ви позволява да се съсредоточите върху изграждането на динамични и отзивчиви интерфейси.

Куката useEventListener, също включена в този пакет, интелигентно следи за събития за преоразмеряване на прозореца. Всеки път, когато размерът на прозореца се промени, useWindowSize актуализира състоянието с най-новите размери, задействайки повторно изобразяване на консумиращия компонент. Това гарантира, че вашият потребителски интерфейс остава синхронизиран със средата за гледане на потребителя, което води до по-завладяващо и полирано потребителско изживяване.

Куката useWindowSize може да се използва в различни сценарии. Това е особено удобно при изграждане на адаптивни оформления, които се адаптират към различни размери на екрана. С тази кука можете без усилие да коригирате стила, оформлението или съдържанието на вашите компоненти въз основа на наличното пространство на прозореца. Освен това, той ви позволява динамично да изобразявате или скривате елементи, да оптимизирате зареждането на изображения или да изпълнявате всяко друго поведение, което разчита на размерите на прозореца.

import useWindowSize from "./useWindowSize"

export default function WindowSizeComponent() {
    const { width, height } = useWindowSize()
    return (
        <div>
            {width} x {height}
        </div>
    )
}

🏆 Награди

Класация #Dev: Глобален ТОП 200 (Сертификат)

Контакти

Имам ясен фокус върху времето за пускане на пазара и не давам приоритет на техническия дълг. И взех участие в дейността Pre-Sale/RFX като системен архитект, усилия за оценка на Frontend (React-TypeScript) и Backend (NodeJS-.NET-PHP-Kafka-SQL-NoSQL). Освен това формирах работата по предварителна продажба като технически директор от възможност до предложение чрез трансфер на знания до успешна доставка.

🛩️ #startups #management #cto #swift #typescript #database

📧 Имейл: [email protected]

👋 LinkedIn: https://linkedin.com/in/sergeyleschev

👋 Twitter: https://twitter.com/sergeyleschev

👋 Github: https://github.com/sergeyleschev

🌎 Уебсайт: https://sergeyleschev.github.io

🌎 Общност на DEV: https://dev.to/sergeyleschev

🌎 Reddit: https://reddit.com/user/sergeyleschev

🌎 Quora: https://quora.com/sergey-leschev

🌎 Среден: https://medium.com/@sergeyleschev

🖨️ PDF: Изтегляне