Optimaliseer React Hooks: Technieken en Best Practices 2026

SAMENVATTING

Modern React Hooks Optimalisatie 2026

Geavanceerde technieken voor React performance optimalisatie met hooks

Keywords: useMemo, useCallback, Custom Hooks


INHOUDSOPGAVE

1 React Hooks Performance: De Moderne Realiteit

2 useMemo Masterclass: Memory Optimalisatie

3 useCallback: Function Referentie Optimalisatie

4 useEffect Performance Patronen

5 Custom Hooks: Herbruikbare Logica

6 React 19 Hooks Updates en Toekomst


MODERNE REACT ONTWIKKELING

React Hooks Performance: De Moderne Realiteit


React Hooks hebben de manier waarop we React applicaties bouwen fundamenteel veranderd sinds hun introductie in 2018. In 2026 zijn ze niet alleen de standaard geworden, maar ook de basis voor geavanceerde performance optimalisaties die voorheen alleen mogelijk waren met class components en complexe state management.

“Performance optimalisatie begint bij het begrijpen van wanneer React herrendert”

— React Team, Performance Best Practices 2026


KERNPUNT

Moderne React applicaties renderen gemiddeld 40% vaker dan nodig door inefficiënte hook implementaties. Correcte optimalisatie kan de performance met 60-80% verbeteren.


De meest voorkomende performance problemen in React applicaties komen voort uit:

Veelvoorkomende Performance Bottlenecks

Onnodige Re-renders — Components die renderen zonder state veranderingen

Zware Berekeningen — Complexe operaties bij elke render cyclus

Function Recreation — Nieuwe functie instanties bij elke render

“>Memory Leaks — Niet proper gecleande useEffect dependencies

React performance monitoring dashboard with render statistics


GEHEUGEN OPTIMALISATIE

useMemo Masterclass: Memory Optimalisatie


useMemo is een van de meest krachtige maar ook meest misbruikte hooks in React. Deze hook memoïzeert de resultaten van dure berekeningen en voorkomt dat deze bij elke render opnieuw uitgevoerd worden.

PROBLEEM 01

Zware Berekeningen Bij Elke Render

Een veelvoorkomend probleem is het uitvoeren van dure berekeningen zoals array filtering of data transformaties bij elke component render, zelfs wanneer de onderliggende data niet veranderd is.

OPLOSSING — useMemo implementatie voor dure berekeningen

CODE-UITLEG

Deze code toont hoe je useMemo gebruikt om zware filtering operaties te memoïzeren. Zonder useMemo wordt de filter functie bij elke render uitgevoerd.


import React, { useMemo, useState } from 'react';

function ExpensiveComponent({ data, searchTerm }) {
  // ❌ Slecht: Filter wordt bij elke render uitgevoerd
  // const filteredData = data.filter(item => 
  //   item.name.toLowerCase().includes(searchTerm.toLowerCase())
  // );

  // ✅ Goed: Memoïzeer de filtering operatie
  const filteredData = useMemo(() => {
    console.log('Filtering data...'); // Log om memoization te testen
    return data.filter(item => 
      item.name.toLowerCase().includes(searchTerm.toLowerCase())
    );
  }, [data, searchTerm]); // Dependencies: data en searchTerm

  const expensiveCalculation = useMemo(() => {
    // Simuleer zware berekening
    let result = 0;
    for (let i = 0; i < filteredData.length * 1000; i++) {
      result += Math.random();
    }
    return result;
  }, [filteredData]);

  return (
    <div>
      <p>Filtered items: {filteredData.length}</p>
      <p>Calculation result: {expensiveCalculation.toFixed(2)}</p>
      {filteredData.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
}

KERNPUNT

useMemo heeft ook overhead. Gebruik het alleen voor berekeningen die meer kosten dan de memoization zelf. Bij simpele berekeningen kan useMemo juist slower maken.


Geavanceerde useMemo Patronen

CODE-UITLEG

Complexere useMemo patronen voor object memoization en deep comparison scenarios.


import React, { useMemo } from 'react';
import { isEqual } from 'lodash';

function AdvancedMemoExample({ config, userPreferences }) {
  // Object memoization met deep comparison
  const processedConfig = useMemo(() => {
    return {
      theme: config.theme || 'light',
      language: userPreferences.language || 'nl',
      features: {
        ...config.features,
        customizations: userPreferences.customizations
      },
      computedStyles: generateStyles(config.theme, userPreferences)
    };
  }, [config, userPreferences]);

  // Memoization met custom comparison
  const complexData = useMemo(() => {
    const previousData = complexData;
    const newData = transformComplexData(config);
    
    // Alleen herberekenen bij daadwerkelijke veranderingen
    return isEqual(previousData, newData) ? previousData : newData;
  }, [config]);

  // Conditional memoization gebaseerd op feature flags
  const conditionalData = useMemo(() => {
    if (!config.features?.experimentalMode) {
      return null;
    }
    
    return heavyExperimentalCalculation(userPreferences);
  }, [config.features?.experimentalMode, userPreferences]);

  return (
    <div>
      <ConfigDisplay config={processedConfig} />
      {conditionalData && <ExperimentalFeature data={conditionalData} />}
    </div>
  );
}

function generateStyles(theme, preferences) {
  // Dure style berekening
  return {
    primaryColor: theme === 'dark' ? '#667eea' : '#4c63d2',
    fontSize: preferences.accessibility?.largeFonts ? '18px' : '16px',
    // ... meer style berekeningen
  };
}

useMemo performance comparison diagram showing render time improvements


FUNCTIE OPTIMALISATIE

useCallback: Function Referentie Optimalisatie


useCallback is essentieel voor het voorkomen van onnodige re-renders van child components. Het memoïzeert function referenties en voorkomt dat nieuwe functies gecreëerd worden bij elke render.

“useCallback is cruciaal bij het doorgeven van functies aan geoptimaliseerde child components”

— React Performance Guide 2026


PROBLEEM 02

Onnodige Child Component Re-renders

Wanneer je functies doorgeeft aan child components worden deze functies bij elke render opnieuw gecreëerd, waardoor React.memo en andere optimalisaties niet werken.

OPLOSSING — useCallback voor stable function references

CODE-UITLEG

Dit voorbeeld toont hoe useCallback voorkomt dat child components onnodige re-renders uitvoeren door stabiele function references te behouden.


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

// Child component geoptimaliseerd met React.memo
const OptimizedChild = memo(({ onClick, data }) => {
  console.log('Child component rendering...');
  
  return (
    <div>
      <p>Data: {data}</p>
      <button onClick={onClick}>Click me</button>
    </div>
  );
});

function ParentComponent() {
  const [count, setCount] = useState(0);
  const [otherState, setOtherState] = useState('initial');

  // ❌ Slecht: Nieuwe functie bij elke render
  // const handleClick = () => {
  //   setCount(prev => prev + 1);
  // };

  // ✅ Goed: Memoïzeer de functie met useCallback
  const handleClick = useCallback(() => {
    setCount(prev => prev + 1);
  }, []); // Lege dependencies = functie verandert nooit

  // useCallback met dependencies
  const handleComplexClick = useCallback((increment) => {
    setCount(prev => prev + increment);
    
    // Logic die afhankelijk is van otherState
    if (otherState === 'special') {
      setCount(prev => prev * 2);
    }
  }, [otherState]); // Hermaak functie alleen als otherState verandert

  return (
    <div>
      <p>Count: {count}</p>
      <p>Other state: {otherState}</p>
      
      {/* Deze component re-rendert NIET bij count changes */}
      <OptimizedChild onClick={handleClick} data="static data" />
      
      <button onClick={() => setOtherState('updated')}>
        Update other state
      </button>
    </div>
  );
}

useCallback Geavanceerde Patronen

CODE-UITLEG

Geavanceerde useCallback technieken voor event handlers, API calls en conditionale logic.


import React, { useState, useCallback, useRef } from 'react';

function AdvancedCallbackExample() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(false);
  const [searchTerm, setSearchTerm] = useState('');
  const abortControllerRef = useRef(null);

  // API call met abort functionality
  const fetchUsers = useCallback(async (query) => {
    // Cancel vorige request
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }

    const controller = new AbortController();
    abortControllerRef.current = controller;

    setLoading(true);
    try {
      const response = await fetch(`/api/users?search=${query}`, {
        signal: controller.signal
      });
      const data = await response.json();
      setUsers(data);
    } catch (error) {
      if (error.name !== 'AbortError') {
        console.error('Fetch error:', error);
      }
    } finally {
      setLoading(false);
    }
  }, []); // Geen dependencies = functie verandert nooit

  // Debounced search handler
  const debouncedSearch = useCallback((term) => {
    const timeoutId = setTimeout(() => {
      fetchUsers(term);
    }, 300);

    return () => clearTimeout(timeoutId);
  }, [fetchUsers]);

  // Event handler met extra parameters
  const handleUserAction = useCallback((userId, action) => {
    return (event) => {
      event.preventDefault();
      
      setUsers(prevUsers => 
        prevUsers.map(user => 
          user.id === userId 
            ? { ...user, status: action }
            : user
        )
      );
    };
  }, []); // Geen dependencies door gebruik van functional updates

  // Conditional callback
  const handleConditionalAction = useCallback((data) => {
    if (!data || loading) return;
    
    // Actie uitvoeren alleen onder specifieke condities
    if (users.length > 10) {
      console.warn('Too many users, action prevented');
      return;
    }
    
    fetchUsers(searchTerm);
  }, [loading, users.length, searchTerm, fetchUsers]);

  return (
    <div>
      <input 
        value={searchTerm}
        onChange={(e) => {
          setSearchTerm(e.target.value);
          debouncedSearch(e.target.value);
        }}
        placeholder="Search users..."
      />
      
      {loading && <p>Loading...</p>}
      
      {users.map(user => (
        <div key={user.id}>
          {user.name}
          <button onClick={handleUserAction(user.id, 'active')}>
            Activate
          </button>
          <button onClick={handleUserAction(user.id, 'inactive')}>
            Deactivate
          </button>
        </div>
      ))}
    </div>
  );
}

KERNPUNT

useCallback is alleen nuttig wanneer de gememoïzeerde functie doorgegeven wordt aan geoptimaliseerde child components of gebruikt wordt als dependency in andere hooks.

useCallback function reference stability diagram


EFFECT OPTIMALISATIE

useEffect Performance Patronen


useEffect optimalisatie gaat verder dan alleen cleanup functions. Het draait om intelligent dependency management, effect splitting en het vermijden van infinite loops.

PROBLEEM 03

Inefficiënte Effect Dependencies

Veel ontwikkelaars voegen onnodige dependencies toe aan useEffect, waardoor effects vaker draaien dan nodig of zelfs infinite loops ontstaan.

OPLOSSING — Smart dependency management

CODE-UITLEG

Deze voorbeelden tonen hoe je useEffect dependencies optimaliseert en infinite loops vermijdt.


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

function OptimizedEffectExample({ userId, config }) {
  const [userData, setUserData] = useState(null);
  const [loading, setLoading] = useState(false);
  const previousUserIdRef = useRef();

  // ❌ Slecht: Object in dependencies
  // useEffect(() => {
  //   fetchUserData(userId, config);
  // }, [userId, config]); // config object verandert elke render!

  // ✅ Goed: Specifieke properties als dependencies
  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const response = await fetch(`/api/users/${userId}`, {
          headers: {
            'Accept-Language': config.language,
            'Theme': config.theme
          }
        });
        const data = await response.json();
        setUserData(data);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [userId, config.language, config.theme]); // Alleen specifieke properties

  // Conditional effect execution
  useEffect(() => {
    // Skip als userId hetzelfde is
    if (previousUserIdRef.current === userId) {
      return;
    }

    previousUserIdRef.current = userId;
    
    // Expensive operation alleen bij userId change
    performExpensiveUserAnalytics(userId);
  }, [userId]);

  // Effect splitting voor betere performance
  useEffect(() => {
    // Effect voor theme changes
    document.body.className = config.theme;
  }, [config.theme]);

  useEffect(() => {
    // Separaat effect voor language changes
    document.documentElement.lang = config.language;
  }, [config.language]);

  // Cleanup pattern voor subscriptions
  useEffect(() => {
    const subscription = subscribeToUserUpdates(userId, (update) => {
      setUserData(prevData => ({ ...prevData, ...update }));
    });

    return () => {
      subscription.unsubscribe();
    };
  }, [userId]); // Alleen userId dependency

  return (
    <div>
      {loading ? <p>Loading...</p> : <UserProfile data={userData} />}
    </div>
  );
}

// Helper function buiten component (voorkomt recreatie)
async function performExpensiveUserAnalytics(userId) {
  // Zware analytische berekeningen
  const analytics = await calculateUserMetrics(userId);
  sendAnalyticsToServer(analytics);
}

Advanced useEffect Patterns

CODE-UITLEG

Geavanceerde useEffect patronen voor debouncing, interval management en complex state synchronization.


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

function AdvancedEffectPatterns() {
  const [searchTerm, setSearchTerm] = useState('');
  const [results, setResults] = useState([]);
  const [isOnline, setIsOnline] = useState(navigator.onLine);
  const searchTimeoutRef = useRef(null);
  const intervalRef = useRef(null);

  // Debounced search effect
  useEffect(() => {
    if (searchTimeoutRef.current) {
      clearTimeout(searchTimeoutRef.current);
    }

    searchTimeoutRef.current = setTimeout(() => {
      if (searchTerm.trim()) {
        performSearch(searchTerm);
      } else {
        setResults([]);
      }
    }, 300);

    return () => {
      if (searchTimeoutRef.current) {
        clearTimeout(searchTimeoutRef.current);
      }
    };
  }, [searchTerm]);

  // Network status monitoring
  useEffect(() => {
    const handleOnline = () => setIsOnline(true);
    const handleOffline = () => setIsOnline(false);

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []); // Geen dependencies = mount/unmount effect

  // Conditional interval effect
  useEffect(() => {
    if (!isOnline) {
      return; // Geen interval als offline
    }

    intervalRef.current = setInterval(() => {
      // Sync data elke 30 seconden
      syncDataWithServer();
    }, 30000);

    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    };
  }, [isOnline]); // Re-setup interval bij network status change

  // Effect met async cleanup
  useEffect(() => {
    let isMounted = true;
    
    const asyncOperation = async () => {
      try {
        const data = await fetchInitialData();
        
        // Check of component nog gemount is
        if (isMounted) {
          setResults(data);
        }
      } catch (error) {
        if (isMounted) {
          console.error('Failed to fetch data:', error);
        }
      }
    };

    asyncOperation();

    return () => {
      isMounted = false; // Cleanup flag
    };
  }, []);

  const performSearch = useCallback(async (term) => {
    try {
      const response = await fetch(`/api/search?q=${encodeURIComponent(term)}`);
      const data = await response.json();
      setResults(data);
    } catch (error) {
      console.error('Search failed:', error);
    }
  }, []);

  return (
    <div>
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="Search..."
      />
      
      {!isOnline && (
        <div style={{ background: '#fff0f0', padding: '10px' }}>
          Offline mode - some features may not work
        </div>
      )}
      
      <div>
        {results.map(item => (
          <div key={item.id}>{item.title}</div>
        ))}
      </div>
    </div>
  );
}

useEffect optimization flowchart with cleanup and dependency management


HERBRUIKBARE LOGICA

Custom Hooks: Herbruikbare Logica


Custom hooks zijn de sleutel tot herbruikbare, testbare en onderhoudbare React code. Ze combineren meerdere built-in hooks in krachtige, geoptimaliseerde abstractions.

“Custom hooks zijn waar de echte kracht van React hooks naar voren komt”

— Dan Abramov, React Core Team


Performance-Geoptimaliseerde Custom Hooks

CODE-UITLEG

Deze custom hooks implementeren geavanceerde performance optimalisaties en herbruikbare patronen.


import { useState, useEffect, useCallback, useMemo, useRef } from 'react';

// 1. useDebounce - Geoptimaliseerde debouncing
function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);
  const timerRef = useRef(null);

  useEffect(() => {
    if (timerRef.current) {
      clearTimeout(timerRef.current);
    }

    timerRef.current = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      if (timerRef.current) {
        clearTimeout(timerRef.current);
      }
    };
  }, [value, delay]);

  return debouncedValue;
}

// 2. useApiData - Gecached API calls
function useApiData(url, options = {}) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const cacheRef = useRef(new Map());
  const abortControllerRef = useRef(null);

  const fetchData = useCallback(async (fetchUrl, fetchOptions) => {
    const cacheKey = JSON.stringify({ url: fetchUrl, ...fetchOptions });
    
    // Return cached data if available
    if (cacheRef.current.has(cacheKey)) {
      const cached = cacheRef.current.get(cacheKey);
      if (Date.now() - cached.timestamp < 300000) { // 5 min cache
        setData(cached.data);
        return;
      }
    }

    // Cancel previous request
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }

    const controller = new AbortController();
    abortControllerRef.current = controller;

    setLoading(true);
    setError(null);

    try {
      const response = await fetch(fetchUrl, {
        ...fetchOptions,
        signal: controller.signal
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const result = await response.json();
      
      // Cache the result
      cacheRef.current.set(cacheKey, {
        data: result,
        timestamp: Date.now()
      });

      setData(result);
    } catch (err) {
      if (err.name !== 'AbortError') {
        setError(err.message);
      }
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    if (url) {
      fetchData(url, options);
    }
  }, [url, options, fetchData]);

  const refetch = useCallback(() => {
    if (url) {
      fetchData(url, options);
    }
  }, [url, options, fetchData]);

  return { data, loading, error, refetch };
}

// 3. useLocalStorage - Geoptimaliseerde localStorage
function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.warn(`Error reading localStorage key "${key}":`, error);
      return initialValue;
    }
  });

  const setValue = useCallback((value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.warn(`Error setting localStorage key "${key}":`, error);
    }
  }, [key, storedValue]);

  return [storedValue, setValue];
}

// 4. useIntersectionObserver - Lazy loading optimization
function useIntersectionObserver(options = {}) {
  const [entry, setEntry] = useState(null);
  const elementRef = useRef(null);
  const observerRef = useRef(null);

  const { threshold = 0, rootMargin = '0px' } = options;

  useEffect(() => {
    const element = elementRef.current;
    
    if (!element || !('IntersectionObserver' in window)) {
      return;
    }

    observerRef.current = new IntersectionObserver(
      ([entry]) => setEntry(entry),
      { threshold, rootMargin }
    );

    observerRef.current.observe(element);

    return () => {
      if (observerRef.current) {
        observerRef.current.disconnect();
      }
    };
  }, [threshold, rootMargin]);

  return [elementRef, entry];
}

Custom Hook Implementatie Voorbeelden

CODE-UITLEG

Praktische implementatie van de custom hooks in React components voor optimale performance.


import React from 'react';

// Component die custom hooks gebruikt
function SearchComponent() {
  const [searchTerm, setSearchTerm] = useLocalStorage('searchTerm', '');
  const debouncedSearch = useDebounce(searchTerm, 300);
  
  const { data: searchResults, loading, error, refetch } = useApiData(
    debouncedSearch ? `/api/search?q=${encodeURIComponent(debouncedSearch)}` : null
  );

  return (
    <div>
      <input
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="Search products..."
      />
      
      {loading && <p>Searching...</p>}
      {error && (
        <div>
          <p>Error: {error}</p>
          <button onClick={refetch}>Retry</button>
        </div>
      )}
      
      {searchResults && (
        <div>
          {searchResults.map(item => (
            <div key={item.id}>{item.title}</div>
          ))}
        </div>
      )}
    </div>
  );
}

// Lazy loading component
function LazyImageComponent({ src, alt }) {
  const [imgRef, entry] = useIntersectionObserver({
    threshold: 0.1,
    rootMargin: '50px'
  });

  const isVisible = entry?.isIntersecting ?? false;

  return (
    <div ref={imgRef} style={{ minHeight: '200px' }}>
      {isVisible ? (
        <img src={src} alt={alt} style={{ width: '100%' }} />
      ) : (
        <div style={{ background: '#f0f0f0', height: '200px' }}>
          Loading...
        </div>
      )}
    </div>
  );
}

Voordelen van Custom Hooks

✓ Herbruikbare logica tussen components

✓ Betere testbaarheid door isolatie

✓ Performance optimalisaties centraal gemanaged

✓ Cleaner component code

KERNPUNT

Custom hooks moeten altijd beginnen met ‘use’ en kunnen andere hooks gebruiken. Ze zijn puur JavaScript functies die React hook rules volgen.

Custom React hooks architecture showing shared logic between components


TOEKOMST VAN REACT

React 19 Hooks Updates en Toekomst


React 19 brengt significante verbeteringen aan het hooks ecosysteem, met focus op automatic batching, concurrent features en nieuwe performance optimalisaties die ontwikkelaars minder handmatige optimalisatie laten doen.

React 19 Nieuwe Features

useActionState — Server actions met loading states

useOptimistic — Optimistic updates voor betere UX

use() — Resource reading in components

“>Auto-batching — Automatische re-render batching


CODE-UITLEG

Voorbeelden van React 19 nieuwe hooks en hoe ze performance verbeteren.


import React, { useActionState, useOptimistic, use } from 'react';

// React 19: useActionState voor server actions
function FormWithActionState() {
  const [state, submitAction, isPending] = useActionState(
    async (prevState, formData) => {
      // Server actie
      try {
        const response = await fetch('/api/submit', {
          method: 'POST',
          body: formData
        });
        
        if (!response.ok) {
          throw new Error('Submission failed');
        }
        
        return { success: true, message: 'Submitted successfully!' };
      } catch (error) {
        return { success: false, message: error.message };
      }
    },
    { success: null, message: '' }
  );

  return (
    <form action={submitAction}>
      <input name="email" type="email" required />
      <button type="submit" disabled={isPending}>
        {isPending ? 'Submitting...' : 'Submit'}
      </button>
      
      {state.success === true && (
        <p style={{ color: 'green' }}>{state.message}</p>
      )}
      {state.success === false && (
        <p style={{ color: 'red' }}>{state.message}</p>
      )}
    </form>
  );
}

// React 19: useOptimistic voor instant UI updates
function OptimisticTodoList({ todos }) {
  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (currentTodos, newTodo) => [...currentTodos, { ...newTodo, pending: true }]
  );

  async function addTodo(formData) {
    const newTodo = {
      id: Date.now(),
      text: formData.get('todo'),
      completed: false
    };

    // Optimistically add todo to UI
    addOptimisticTodo(newTodo);

    try {
      // Server call
      await fetch('/api/todos', {
        method: 'POST',
        body: JSON.stringify(newTodo),
        headers: { 'Content-Type': 'application/json' }
      });
    } catch (error) {
      // Error handling - optimistic update will be reverted
      console.error('Failed to add todo:', error);
    }
  }

  return (
    <div>
      <form action={addTodo}>
        <input name="todo" placeholder="Add todo..." required />
        <button type="submit">Add</button>
      </form>
      
      {optimisticTodos.map(todo => (
        <div 
          key={todo.id}
          style={{ 
            opacity: todo.pending ? 0.7 : 1,
            fontStyle: todo.pending ? 'italic' : 'normal'
          }}
        >
          {todo.text}
        </div>
      ))}
    </div>
  );
}

// React 19: use() for resource reading
function UserProfile({ userId }) {
  // use() kan promises en context lezen
  const user = use(fetchUser(userId)); // Promise
  const theme = use(ThemeContext); // Context

  return (
    <div style={{ background: theme.background }}>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

// Promise caching voor use()
const userCache = new Map();
function fetchUser(userId) {
  if (!userCache.has(userId)) {
    userCache.set(userId, 
      fetch(`/api/users/${userId}`).then(res => res.json())
    );
  }
  return userCache.get(userId);
}

85%

Minder handmatige optimalisatie

React 19 automatic optimizations


KERNPUNT

React 19’s automatic batching en concurrent features reduceren de noodzaak voor handmatige useMemo en useCallback optimalisaties met 60-70% in typische applicaties.




Bedankt voor het lezen!

Met deze geavanceerde React Hooks patronen kun je applicaties bouwen die niet alleen sneller zijn, maar ook beter onderhoudbaar. Performance optimalisatie is een continue proces — meet altijd eerst voordat je optimaliseert!

Vragen over React Hooks optimalisatie? Laat een reactie achter!