עברית

גלו את העוצמה של לוגיקה רב-שימושית באפליקציות הריאקט שלכם באמצעות הוקים מותאמים אישית. למדו כיצד ליצור ולמנף אותם לקוד נקי וקל יותר לתחזוקה.

הוקים מותאמים אישית: תבניות לוגיקה רב-שימושיות בריאקט

הוקים (Hooks) של ריאקט חוללו מהפכה באופן שבו אנו כותבים קומפוננטות בריאקט, על ידי הצגת תכונות של מצב (state) ומחזור חיים (lifecycle) לקומפוננטות פונקציונליות. בין היתרונות הרבים שהם מציעים, הוקים מותאמים אישית (custom hooks) בולטים כמנגנון רב-עוצמה לחילוץ ושימוש חוזר בלוגיקה על פני קומפוננטות מרובות. פוסט זה יצלול לעומק עולמם של ההוקים המותאמים אישית, ויסקור את יתרונותיהם, אופן יצירתם ושימושם באמצעות דוגמאות מעשיות.

מהם הוקים מותאמים אישית?

במהותם, הוק מותאם אישית הוא פונקציית JavaScript ששמה מתחיל במילה "use" ויכולה לקרוא להוקים אחרים. הם מאפשרים לחלץ לוגיקה של קומפוננטה לפונקציות רב-שימושיות. זוהי דרך עוצמתית לשתף לוגיקה המנהלת מצב (stateful logic), תופעות לוואי (side effects), או התנהגויות מורכבות אחרות בין קומפוננטות, מבלי להזדקק לתבניות מורכבות כמו render props או higher-order components.

מאפיינים מרכזיים של הוקים מותאמים אישית:

היתרונות בשימוש בהוקים מותאמים אישית

הוקים מותאמים אישית מציעים מספר יתרונות משמעותיים בפיתוח ריאקט:

יצירת ההוק המותאם אישית הראשון שלכם

בואו נדגים את יצירתו של הוק מותאם אישית באמצעות דוגמה מעשית: הוק שעוקב אחר גודל החלון.

דוגמה: 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);

    // Remove event listener on cleanup
    return () => window.removeEventListener('resize', handleResize);
  }, []); // Empty array ensures that effect is only run on mount

  return windowSize;
}

export default useWindowSize;

הסבר:

  1. ייבוא ההוקים הנחוצים: אנו מייבאים את useState ו-useEffect מריאקט.
  2. הגדרת ההוק: אנו יוצרים פונקציה בשם useWindowSize, תוך שמירה על מוסכמת השמות.
  3. אתחול המצב (State): אנו משתמשים ב-useState כדי לאתחל את המצב windowSize עם הרוחב והגובה ההתחלתיים של החלון.
  4. הגדרת מאזין אירועים (Event Listener): אנו משתמשים ב-useEffect כדי להוסיף מאזין לאירוע שינוי גודל (resize) לחלון. כאשר גודל החלון משתנה, הפונקציה handleResize מעדכנת את המצב windowSize.
  5. ניקוי (Cleanup): אנו מחזירים פונקציית ניקוי מ-useEffect כדי להסיר את מאזין האירועים כאשר הקומפוננטה מוסרת (unmounts). הדבר מונע דליפות זיכרון.
  6. החזרת ערכים: ההוק מחזיר את האובייקט windowSize, המכיל את הרוחב והגובה הנוכחיים של החלון.

שימוש בהוק המותאם אישית בקומפוננטה

כעת, לאחר שיצרנו את ההוק המותאם אישית שלנו, בואו נראה כיצד להשתמש בו בקומפוננטת ריאקט.

import React from 'react';
import useWindowSize from './useWindowSize';

function MyComponent() {
  const { width, height } = useWindowSize();

  return (
    

Window width: {width}px

Window height: {height}px

); } export default MyComponent;

הסבר:

  1. ייבוא ההוק: אנו מייבאים את ההוק המותאם אישית useWindowSize.
  2. קריאה להוק: אנו קוראים להוק useWindowSize בתוך הקומפוננטה.
  3. גישה לערכים: אנו מפרקים (destructure) את האובייקט המוחזר כדי לקבל את הערכים width ו-height.
  4. רינדור הערכים: אנו מרנדרים את ערכי הרוחב והגובה בממשק המשתמש של הקומפוננטה.

כל קומפוננטה המשתמשת ב-useWindowSize תתעדכן אוטומטית כאשר גודל החלון ישתנה.

דוגמאות מורכבות יותר

בואו נבחן כמה מקרי שימוש מתקדמים יותר עבור הוקים מותאמים אישית.

דוגמה: useLocalStorage

הוק זה מאפשר לאחסן ולאחזר נתונים בקלות מה-local storage.

import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
  // State to store our value
  // Pass initial value to useState so logic is only executed once
  const [storedValue, setStoredValue] = useState(() => {
    try {
      // Get from local storage by key
      const item = window.localStorage.getItem(key);
      // Parse stored json or if none return initialValue
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      // If error also return initialValue
      console.log(error);
      return initialValue;
    }
  });

  // Return a wrapped version of useState's setter function that ...
  // ... persists the new value to localStorage.
  const setValue = (value) => {
    try {
      // Allow value to be a function so we have same API as useState
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      // Save to local storage
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
      // Save state
      setStoredValue(valueToStore);
    } catch (error) {
      // A more advanced implementation would handle the error case
      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', 'Guest');

  return (
    

Hello, {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 error! status: ${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 

Loading...

; if (error) return

Error: {error.message}

; return (

Title: {data.title}

Completed: {data.completed ? 'Yes' : 'No'}

); } export default MyComponent;

שיטות עבודה מומלצות (Best Practices) להוקים מותאמים אישית

כדי להבטיח שההוקים המותאמים אישית שלכם יהיו יעילים וקלים לתחזוקה, עקבו אחר שיטות העבודה המומלצות הבאות:

מהמורות נפוצות שכדאי להימנע מהן

תבניות מתקדמות

הרכבת הוקים מותאמים אישית

ניתן להרכיב הוקים מותאמים אישית יחד כדי ליצור לוגיקה מורכבת יותר. לדוגמה, ניתן לשלב הוק useLocalStorage עם הוק useFetch כדי לשמור באופן אוטומטי נתונים שנשלפו ב-local storage.

שיתוף לוגיקה בין הוקים

אם מספר הוקים מותאמים אישית חולקים לוגיקה משותפת, ניתן לחלץ לוגיקה זו לפונקציית שירות (utility function) נפרדת ולהשתמש בה בשני ההוקים.

שימוש ב-Context עם הוקים מותאמים אישית

ניתן להשתמש בהוקים מותאמים אישית בשילוב עם React Context כדי לגשת ולעדכן מצב גלובלי. הדבר מאפשר ליצור קומפוננטות רב-שימושיות המודעות למצב הגלובלי של האפליקציה ויכולות לתקשר איתו.

דוגמאות מהעולם האמיתי

הנה כמה דוגמאות לאופן שבו ניתן להשתמש בהוקים מותאמים אישית ביישומים בעולם האמיתי:

דוגמה: הוק 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: 'Geolocation is not supported by this browser.',
      });
      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;

סיכום

הוקים מותאמים אישית הם כלי רב-עוצמה לכתיבת קוד ריאקט נקי, רב-שימושי וקל יותר לתחזוקה. על ידי כימוס לוגיקה מורכבת בהוקים מותאמים אישית, ניתן לפשט את הקומפוננטות, להפחית שכפול קוד ולשפר את המבנה הכללי של האפליקציות שלכם. אמצו את ההוקים המותאמים אישית וגלו את הפוטנציאל שלהם לבניית אפליקציות ריאקט חזקות וסקיילביליות יותר.

התחילו בזיהוי אזורים בבסיס הקוד הקיים שלכם שבהם לוגיקה חוזרת על עצמה על פני מספר קומפוננטות. לאחר מכן, בצעו ריפקטורינג ללוגיקה זו והעבירו אותה להוקים מותאמים אישית. עם הזמן, תבנו ספרייה של הוקים רב-שימושיים שתאיץ את תהליך הפיתוח שלכם ותשפר את איכות הקוד שלכם.

זכרו לעקוב אחר שיטות עבודה מומלצות, להימנע ממהמורות נפוצות ולחקור תבניות מתקדמות כדי להפיק את המרב מהוקים מותאמים אישית. עם תרגול וניסיון, תהפכו למומחים בהוקים מותאמים אישית ולמפתחי ריאקט יעילים יותר.