Отключете силата на преизползваемата логика във вашите React приложения с персонализирани куки. Научете как да създавате и използвате персонализирани куки за по-чист и лесен за поддръжка код.
Персонализирани куки (Custom Hooks): Модели за преизползваема логика в React
React куките (Hooks) революционизираха начина, по който пишем React компоненти, като въведоха състояние (state) и функции на жизнения цикъл (lifecycle) във функционалните компоненти. Сред многото предимства, които предлагат, персонализираните куки се открояват като мощен механизъм за извличане и повторно използване на логика в множество компоненти. Тази блог публикация ще се потопи дълбоко в света на персонализираните куки, изследвайки техните предимства, създаване и използване с практически примери.
Какво представляват персонализираните куки?
По същество, персонализираната кука е JavaScript функция, която започва с думата "use" и може да извиква други куки. Те ви позволяват да извличате логиката на компонента в преизползваеми функции. Това е мощен начин за споделяне на логика със състояние, странични ефекти или други сложни поведения между компоненти, без да се прибягва до render props, компоненти от по-висок ред (higher-order components) или други сложни модели.
Ключови характеристики на персонализираните куки:
- Конвенция за именуване: Персонализираните куки трябва да започват с думата "use". Това сигнализира на React, че функцията съдържа куки и трябва да спазва правилата на куките.
- Преизползваемост: Основната цел е да се капсулира преизползваема логика, което улеснява споделянето на функционалност между компоненти.
- Логика със състояние: Персонализираните куки могат да управляват собствено си състояние, използвайки куката
useState
, което им позволява да капсулират сложно поведение със състояние. - Странични ефекти: Те могат също така да извършват странични ефекти, използвайки куката
useEffect
, което позволява интеграция с външни API-та, извличане на данни и други. - Композируемост: Персонализираните куки могат да извикват други куки, което ви позволява да изграждате сложна логика чрез композиране на по-малки, по-фокусирани куки.
Предимства от използването на персонализирани куки
Персонализираните куки предлагат няколко значителни предимства в разработката с React:
- Преизползваемост на кода: Най-очевидното предимство е възможността за повторно използване на логика в множество компоненти. Това намалява дублирането на код и насърчава по-суха (Don't Repeat Yourself - DRY) кодова база.
- Подобрена четимост: Чрез извличане на сложна логика в отделни персонализирани куки, вашите компоненти стават по-чисти и лесни за разбиране. Основната логика на компонента остава фокусирана върху изобразяването на потребителския интерфейс.
- Подобрена поддръжка: Когато логиката е капсулирана в персонализирани куки, промените и поправките на грешки могат да се прилагат на едно място, намалявайки риска от въвеждане на грешки в множество компоненти.
- Тестируемост: Персонализираните куки могат лесно да се тестват изолирано, което гарантира, че преизползваемата логика функционира правилно, независимо от компонентите, които я използват.
- Опростени компоненти: Персонализираните куки помагат да се изчистят компонентите, като ги правят по-малко многословни и по-фокусирани върху основната им цел.
Създаване на първата ви персонализирана кука
Нека илюстрираме създаването на персонализирана кука с практически пример: кука, която проследява размера на прозореца.
Пример: useWindowSize
Тази кука ще връща текущата ширина и височина на прозореца на браузъра. Тя също така ще актуализира тези стойности, когато прозорецът бъде преоразмерен.
import { useState, useEffect } from 'react';
function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
window.addEventListener('resize', handleResize);
// Премахване на event listener при почистване
return () => window.removeEventListener('resize', handleResize);
}, []); // Празният масив гарантира, че ефектът се изпълнява само при монтиране
return windowSize;
}
export default useWindowSize;
Обяснение:
- Импортиране на необходимите куки: Импортираме
useState
иuseEffect
от React. - Дефиниране на куката: Създаваме функция с име
useWindowSize
, спазвайки конвенцията за именуване. - Инициализиране на състоянието: Използваме
useState
, за да инициализираме състояниетоwindowSize
с началната ширина и височина на прозореца. - Настройване на Event Listener: Използваме
useEffect
, за да добавим event listener за преоразмеряване към прозореца. Когато прозорецът се преоразмери, функциятаhandleResize
актуализира състояниетоwindowSize
. - Почистване: Връщаме функция за почистване от
useEffect
, за да премахнем event listener-а, когато компонентът се демонтира. Това предотвратява изтичане на памет. - Връщане на стойности: Куката връща обекта
windowSize
, съдържащ текущата ширина и височина на прозореца.
Използване на персонализираната кука в компонент
Сега, след като създадохме нашата персонализирана кука, нека видим как да я използваме в React компонент.
import React from 'react';
import useWindowSize from './useWindowSize';
function MyComponent() {
const { width, height } = useWindowSize();
return (
Ширина на прозореца: {width}px
Височина на прозореца: {height}px
);
}
export default MyComponent;
Обяснение:
- Импортиране на куката: Импортираме персонализираната кука
useWindowSize
. - Извикване на куката: Извикваме куката
useWindowSize
в рамките на компонента. - Достъп до стойностите: Деструктурираме върнатия обект, за да получим стойностите
width
иheight
. - Изобразяване на стойностите: Изобразяваме стойностите за ширина и височина в потребителския интерфейс на компонента.
Всеки компонент, който използва useWindowSize
, ще се актуализира автоматично, когато размерът на прозореца се промени.
По-сложни примери
Нека разгледаме някои по-напреднали случаи на употреба на персонализирани куки.
Пример: useLocalStorage
Тази кука ви позволява лесно да съхранявате и извличате данни от локалното хранилище (local storage).
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// Състояние за съхраняване на нашата стойност
// Предаваме начална стойност на useState, така че логиката да се изпълни само веднъж
const [storedValue, setStoredValue] = useState(() => {
try {
// Вземане от локалното хранилище по ключ
const item = window.localStorage.getItem(key);
// Парсване на съхранения json или ако няма такъв, връщане на initialValue
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// При грешка също връщаме initialValue
console.log(error);
return initialValue;
}
});
// Връщаме обвита версия на setter функцията на useState, която...
// ... запазва новата стойност в localStorage.
const setValue = (value) => {
try {
// Позволяваме стойността да бъде функция, за да имаме същия API като useState
const valueToStore = value instanceof Function ? value(storedValue) : value;
// Запазване в локалното хранилище
window.localStorage.setItem(key, JSON.stringify(valueToStore));
// Запазване на състоянието
setStoredValue(valueToStore);
} catch (error) {
// По-напреднала имплементация би обработила случая с грешка
console.log(error);
}
};
useEffect(() => {
try {
const item = window.localStorage.getItem(key);
setStoredValue(item ? JSON.parse(item) : initialValue);
} catch (error) {
console.log(error);
}
}, [key, initialValue]);
return [storedValue, setValue];
}
export default useLocalStorage;
Употреба:
import React from 'react';
import useLocalStorage from './useLocalStorage';
function MyComponent() {
const [name, setName] = useLocalStorage('name', 'Гост');
return (
Здравейте, {name}!
setName(e.target.value)}
/>
);
}
export default MyComponent;
Пример: useFetch
Тази кука капсулира логиката за извличане на данни от API.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP грешка! статус: ${response.status}`);
}
const json = await response.json();
setData(json);
setLoading(false);
} catch (error) {
setError(error);
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
Употреба:
import React from 'react';
import useFetch from './useFetch';
function MyComponent() {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/todos/1');
if (loading) return Зареждане...
;
if (error) return Грешка: {error.message}
;
return (
Заглавие: {data.title}
Завършено: {data.completed ? 'Да' : 'Не'}
);
}
export default MyComponent;
Най-добри практики за персонализирани куки
За да сте сигурни, че вашите персонализирани куки са ефективни и лесни за поддръжка, следвайте тези най-добри практики:
- Поддържайте ги фокусирани: Всяка персонализирана кука трябва да има една-единствена, добре дефинирана цел. Избягвайте създаването на прекалено сложни куки, които се опитват да правят твърде много.
- Документирайте вашите куки: Предоставяйте ясна и сбита документация за всяка персонализирана кука, обясняваща нейната цел, входни данни и изходни резултати.
- Тествайте вашите куки: Пишете единични тестове (unit tests) за вашите персонализирани куки, за да се уверите, че функционират правилно и надеждно.
- Използвайте описателни имена: Избирайте описателни имена за вашите персонализирани куки, които ясно показват тяхната цел.
- Обработвайте грешките елегантно: Внедрете обработка на грешки във вашите персонализирани куки, за да предотвратите неочаквано поведение и да предоставите информативни съобщения за грешки.
- Мислете за преизползваемостта: Проектирайте вашите персонализирани куки с мисъл за преизползваемост. Направете ги достатъчно общи, за да могат да се използват в множество компоненти.
- Избягвайте прекомерната абстракция: Не създавайте персонализирани куки за проста логика, която може лесно да се обработи в рамките на компонент. Извличайте само логика, която е наистина преизползваема и сложна.
Често срещани капани, които да избягвате
- Нарушаване на правилата на куките: Винаги извиквайте куките на най-горното ниво на вашата функция за персонализирана кука и ги извиквайте само от функционални компоненти на React или други персонализирани куки.
- Игнориране на зависимости в useEffect: Уверете се, че включвате всички необходими зависимости в масива със зависимости на куката
useEffect
, за да предотвратите остарели затваряния (stale closures) и неочаквано поведение. - Създаване на безкрайни цикли: Бъдете внимателни, когато актуализирате състояние в кука
useEffect
, тъй като това лесно може да доведе до безкрайни цикли. Уверете се, че актуализацията е условна и се основава на промени в зависимостите. - Забравяне на почистването: Винаги включвайте функция за почистване в
useEffect
, за да премахвате event listeners, да отменяте абонаменти и да извършвате други задачи по почистване, за да предотвратите изтичане на памет.
Напреднали модели
Композиране на персонализирани куки
Персонализираните куки могат да се композират заедно, за да се създаде по-сложна логика. Например, можете да комбинирате кука useLocalStorage
с кука useFetch
, за да запазвате автоматично извлечените данни в локалното хранилище.
Споделяне на логика между куки
Ако няколко персонализирани куки споделят обща логика, можете да извлечете тази логика в отделна помощна функция и да я използвате повторно и в двете куки.
Използване на Context с персонализирани куки
Персонализираните куки могат да се използват в комбинация с React Context за достъп и актуализиране на глобалното състояние. Това ви позволява да създавате преизползваеми компоненти, които са наясно с глобалното състояние на приложението и могат да взаимодействат с него.
Примери от реалния свят
Ето няколко примера за това как персонализираните куки могат да се използват в приложения от реалния свят:
- Валидация на формуляри: Създайте кука
useForm
за обработка на състоянието на формуляра, валидацията и изпращането. - Автентикация: Имплементирайте кука
useAuth
за управление на автентикацията и оторизацията на потребителите. - Управление на теми: Разработете кука
useTheme
за превключване между различни теми (светла, тъмна и т.н.). - Геолокация: Изградете кука
useGeolocation
за проследяване на текущото местоположение на потребителя. - Откриване на превъртане (scroll): Създайте кука
useScroll
, за да откриете кога потребителят е превъртял до определена точка на страницата.
Пример: кука useGeolocation за междукултурни приложения като картографски или куриерски услуги
import { useState, useEffect } from 'react';
function useGeolocation() {
const [location, setLocation] = useState({
latitude: null,
longitude: null,
error: null,
});
useEffect(() => {
if (!navigator.geolocation) {
setLocation({
latitude: null,
longitude: null,
error: 'Геолокацията не се поддържа от този браузър.',
});
return;
}
const watchId = navigator.geolocation.watchPosition(
(position) => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
},
(error) => {
setLocation({
latitude: null,
longitude: null,
error: error.message,
});
}
);
return () => navigator.geolocation.clearWatch(watchId);
}, []);
return location;
}
export default useGeolocation;
Заключение
Персонализираните куки са мощен инструмент за писане на по-чист, по-преизползваем и по-лесен за поддръжка React код. Чрез капсулиране на сложна логика в персонализирани куки, можете да опростите вашите компоненти, да намалите дублирането на код и да подобрите цялостната структура на вашите приложения. Прегърнете персонализираните куки и отключете техния потенциал за изграждане на по-стабилни и мащабируеми React приложения.
Започнете, като идентифицирате области във вашата съществуваща кодова база, където логиката се повтаря в множество компоненти. След това, рефакторирайте тази логика в персонализирани куки. С течение на времето ще изградите библиотека от преизползваеми куки, които ще ускорят процеса ви на разработка и ще подобрят качеството на вашия код.
Не забравяйте да следвате най-добрите практики, да избягвате често срещаните капани и да изследвате напреднали модели, за да извлечете максимума от персонализираните куки. С практика и опит ще станете майстор на персонализираните куки и по-ефективен React разработчик.