Kodax

Hooks الأخرى — مقدمة React

Hooks الأخرى المهمة

useContext - مشاركة البيانات

import { createContext, useContext, useState } from 'react';

// إنشاء Context
const ThemeContext = createContext();

// Provider
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('dark');
  
  const toggleTheme = () => {
    setTheme(prev => prev === 'dark' ? 'light' : 'dark');
  };
  
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// استخدام في أي مكون
function Button() {
  const { theme, toggleTheme } = useContext(ThemeContext);
  
  return (
    <button 
      onClick={toggleTheme}
      className={`btn btn-${theme}`}
    >
      الوضع: {theme}
    </button>
  );
}

// App
function App() {
  return (
    <ThemeProvider>
      <Header />
      <Main />
      <Button />
    </ThemeProvider>
  );
}

useRef - مرجع للعناصر

import { useRef, useEffect } from 'react';

function TextInput() {
  const inputRef = useRef(null);
  
  useEffect(() => {
    // التركيز على الحقل عند التحميل
    inputRef.current.focus();
  }, []);
  
  return (
    <input 
      ref={inputRef}
      placeholder="اكتب هنا..."
    />
  );
}

// حفظ قيمة بدون إعادة render
function Timer() {
  const countRef = useRef(0);
  const [display, setDisplay] = useState(0);
  
  useEffect(() => {
    const timer = setInterval(() => {
      countRef.current += 1;
      console.log(countRef.current); // يتحدث
    }, 1000);
    
    return () => clearInterval(timer);
  }, []);
  
  return (
    <div>
      <p>العداد الداخلي: {countRef.current}</p>
      <button onClick={() => setDisplay(countRef.current)}>
        عرض القيمة الحالية
      </button>
      <p>المعروض: {display}</p>
    </div>
  );
}

useMemo - تخزين القيم المحسوبة

import { useMemo, useState } from 'react';

function ExpensiveList({ items, filter }) {
  // تُنفذ فقط عند تغير items أو filter
  const filteredItems = useMemo(() => {
    console.log("Filtering...");
    return items.filter(item => 
      item.name.includes(filter)
    );
  }, [items, filter]);
  
  return (
    <ul>
      {filteredItems.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

// مثال: حساب معقد
function Calculator({ numbers }) {
  const sum = useMemo(() => {
    return numbers.reduce((acc, n) => acc + n, 0);
  }, [numbers]);
  
  const average = useMemo(() => {
    return sum / numbers.length;
  }, [sum, numbers.length]);
  
  return (
    <div>
      <p>المجموع: {sum}</p>
      <p>المتوسط: {average}</p>
    </div>
  );
}

useCallback - تخزين الدوال

import { useCallback, useState, memo } from 'react';

// مكون ابن مُحسّن
const Button = memo(({ onClick, children }) => {
  console.log("Button rendered:", children);
  return <button onClick={onClick}>{children}</button>;
});

function Parent() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState("");
  
  // ❌ بدون useCallback - تُنشأ دالة جديدة كل render
  const handleClick = () => setCount(c => c + 1);
  
  // ✅ مع useCallback - نفس الدالة
  const handleClickMemo = useCallback(() => {
    setCount(c => c + 1);
  }, []);
  
  return (
    <div>
      <input 
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <p>العداد: {count}</p>
      <Button onClick={handleClickMemo}>زيادة</Button>
    </div>
  );
}

useReducer - للـ State المعقد

import { useReducer } from 'react';

// الحالة الأولية
const initialState = {
  items: [],
  loading: false,
  error: null
};

// Reducer Function
function reducer(state, action) {
  switch (action.type) {
    case 'FETCH_START':
      return { ...state, loading: true, error: null };
    case 'FETCH_SUCCESS':
      return { ...state, loading: false, items: action.payload };
    case 'FETCH_ERROR':
      return { ...state, loading: false, error: action.payload };
    case 'ADD_ITEM':
      return { ...state, items: [...state.items, action.payload] };
    case 'DELETE_ITEM':
      return { 
        ...state, 
        items: state.items.filter(item => item.id !== action.payload)
      };
    default:
      return state;
  }
}

function ItemList() {
  const [state, dispatch] = useReducer(reducer, initialState);
  
  const fetchItems = async () => {
    dispatch({ type: 'FETCH_START' });
    
    try {
      const response = await fetch('/api/items');
      const data = await response.json();
      dispatch({ type: 'FETCH_SUCCESS', payload: data });
    } catch (error) {
      dispatch({ type: 'FETCH_ERROR', payload: error.message });
    }
  };
  
  const addItem = (item) => {
    dispatch({ type: 'ADD_ITEM', payload: item });
  };
  
  const deleteItem = (id) => {
    dispatch({ type: 'DELETE_ITEM', payload: id });
  };
  
  return (
    <div>
      {state.loading && <p>جارٍ التحميل...</p>}
      {state.error && <p>خطأ: {state.error}</p>}
      
      <ul>
        {state.items.map(item => (
          <li key={item.id}>
            {item.name}
            <button onClick={() => deleteItem(item.id)}>حذف</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

Custom Hooks

// Hook مخصص لجلب البيانات
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch(url);
        const json = await response.json();
        setData(json);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };
    
    fetchData();
  }, [url]);
  
  return { data, loading, error };
}

// استخدام
function Users() {
  const { data: users, loading, error } = useFetch('/api/users');
  
  if (loading) return <p>جارٍ التحميل...</p>;
  if (error) return <p>خطأ: {error}</p>;
  
  return (
    <ul>
      {users?.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}
import { useState, useContext, createContext, useMemo } from 'react';

// Auth Context
const AuthContext = createContext();

function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  
  const login = (userData) => setUser(userData);
  const logout = () => setUser(null);
  
  const value = useMemo(() => ({
    user,
    isLoggedIn: !!user,
    login,
    logout
  }), [user]);
  
  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  );
}

// Hook مخصص
function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within AuthProvider');
  }
  return context;
}

// استخدام
function Profile() {
  const { user, isLoggedIn, logout } = useAuth();
  
  if (!isLoggedIn) {
    return <p>الرجاء تسجيل الدخول</p>;
  }
  
  return (
    <div>
      <h2>مرحباً {user.name}</h2>
      <button onClick={logout}>تسجيل الخروج</button>
    </div>
  );
}
اختبر فهمك