slogan3

Guideer mij bij het implementeren van aangepaste hooks in TypeScript voor {{hook_type}}

description

Door deze prompt te volgen, leren gebruikers hoe ze aangepaste hooks kunnen implementeren die de architectuur van hun toepassing verbeteren, waardoor het beheer van staat en bijwerkingen eenvoudiger wordt en typeveiligheid en herbruikbaarheid worden gewaarborgd.

prompt

try_prompt

Help me aangepaste hooks in TypeScript te maken die effectief staat en bijwerkingen kunnen beheren voor {{hook_type}}. Geef alstublieft ... more
generate_helper
disclaimerOnPageApi image_legal_disclaimer...

ai_answers

provider: openai
model: gpt-4.1-nano
Zeker! Hier is een uitgebreide gids voor het maken van aangepaste React-haken in TypeScript die effectief staan en bijwerkingen beheren, specifiek gericht op een `useFetch`-hook. --- ### 1. Doel en overzicht Een `useFetch`-hook maakt het mogelijk om data op te halen en bijbehorende status, foutafhandeling en herhaalmogelijkheden te beheren. Het moet: - Typeveilig zijn - Effectief omgaan met bijwerkingen (zoals aborties) - Herbruikbaar en flexibel - Eenvoudig te gebruiken in componenten --- ### 2. Structuur van de custom hook Een typische `useFetch`-hook bevat: - Status (loading, error, data) - Fetch-logica - Effectief beheer van bijwerkingen (bijvoorbeeld `AbortController`) - Mogelijkheid om opnieuw te fetchen ### 3. Voorbeeld implementatie in TypeScript Hier is een gedetailleerd voorbeeld dat je als uitgangspunt kunt gebruiken: ```typescript import { useState, useEffect, useCallback } from 'react'; interface FetchState<T> { data: T | null; error: Error | null; loading: boolean; } /** * useFetch - custom hook voor data ophalen met bijwerkingenbeheer * @param url - de URL om data op te halen * @param options - fetch opties zoals headers, method, etc. * @returns object met data, error, loading, en een refetch functie */ function useFetch<T = unknown>(url: string, options?: RequestInit) { const [state, setState] = useState<FetchState<T>>({ data: null, error: null, loading: false, }); const fetchData = useCallback(async () => { const controller = new AbortController(); const { signal } = controller; setState({ data: null, error: null, loading: true }); try { const response = await fetch(url, { ...options, signal }); if (!response.ok) { throw new Error(`Fout bij ophalen: ${response.status} ${response.statusText}`); } const data: T = await response.json(); setState({ data, error: null, loading: false }); } catch (error) { if (error instanceof Error && error.name !== 'AbortError') { setState({ data: null, error, loading: false }); } } // Retourneer de controller zodat deze kan worden geaborteerd return () => controller.abort(); }, [url, options]); // Effect voor initiële fetch en bij URL of opties veranderen useEffect(() => { const abortFetch = fetchData(); // Cleanup: abort fetch bij unmount of nieuwe fetch return () => { abortFetch(); }; }, [fetchData]); // Refetch functie voor hergebruik const refetch = useCallback(() => { fetchData(); }, [fetchData]); return { ...state, refetch }; } export default useFetch; ``` --- ### 4. Gebruik in component ```tsx import React from 'react'; import useFetch from './useFetch'; interface User { id: number; name: string; } function UserComponent() { const { data, error, loading, refetch } = useFetch<User[]>('https://api.example.com/users'); if (loading) return <p>Laden...</p>; if (error) return <p>Fout: {error.message}</p>; return ( <div> <h1>Gebruikers</h1> <button onClick={refetch}>Haal opnieuw op</button> <ul> {data?.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); } ``` --- ### 5. Beste praktijken - **Typeveiligheid:** Gebruik generieke types (`<T>`) zodat je precies weet welk type data je verwacht. - **Effectief beheer van bijwerkingen:** Gebruik `AbortController` om fetch te annuleren bij unmounts of nieuwe fetches. - **Herbruikbaarheid:** Maak parameters flexibel (bijvoorbeeld opties), en exposeer functies zoals `refetch`. - **Error handling:** Geef gedetailleerde foutmeldingen door en behandel deze in componenten. - **Caching & optimalisatie:** Voor geavanceerdere gevallen kun je caching of react-query overwegen, maar voor basisgebruik is bovenstaande voldoende. --- ### 6. Samenvatting - Gebruik `useState` en `useEffect` om fetchstatus en data te beheren. - Gebruik `AbortController` om bijwerkingen te beheren. - Maak je hook generiek voor typeveiligheid. - Bied een `refetch`-functie voor hergebruik. - Zorg dat je cleanup goed is afgehandeld. --- Mocht je verdere uitbreidingen willen, zoals caching, polling, of integratie met context, dan kan ik je daar ook mee helpen!
provider: deepseek
model: deepseek-chat
Zeker! Ik help je graag met het maken van aangepaste hooks in TypeScript voor data fetching. Hier is een complete uitleg met voorbeelden en best practices. ## Basis useFetch Hook ```typescript import { useState, useEffect, useCallback } from 'react'; // Basis types voor onze hook interface FetchState<T> { data: T | null; loading: boolean; error: string | null; } interface UseFetchOptions { immediate?: boolean; } export function useFetch<T = unknown>( url: string, options?: RequestInit & UseFetchOptions ) { const [state, setState] = useState<FetchState<T>>({ data: null, loading: false, error: null, }); const fetchData = useCallback(async () => { setState(prev => ({ ...prev, loading: true, error: null })); try { const response = await fetch(url, options); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json() as T; setState({ data, loading: false, error: null }); } catch (error) { setState({ data: null, loading: false, error: error instanceof Error ? error.message : 'An error occurred', }); } }, [url, options]); useEffect(() => { if (options?.immediate !== false) { fetchData(); } }, [fetchData, options?.immediate]); return { ...state, refetch: fetchData, }; } ``` ## Geavanceerde Versie met Meer Functionaliteit ```typescript import { useState, useEffect, useCallback, useRef } from 'react'; // Uitgebreide types interface FetchState<T> { data: T | null; loading: boolean; error: string | null; status: number | null; } interface UseFetchOptions extends RequestInit { immediate?: boolean; onSuccess?: (data: T) => void; onError?: (error: string) => void; } interface UseFetchReturn<T> extends FetchState<T> { refetch: () => Promise<void>; abort: () => void; } export function useFetch<T = unknown>( url: string | null, options: UseFetchOptions = {} ): UseFetchReturn<T> { const [state, setState] = useState<FetchState<T>>({ data: null, loading: false, error: null, status: null, }); const abortControllerRef = useRef<AbortController | null>(null); const fetchData = useCallback(async () => { if (!url) return; // Annuleer vorige request if (abortControllerRef.current) { abortControllerRef.current.abort(); } abortControllerRef.current = new AbortController(); setState(prev => ({ ...prev, loading: true, error: null })); try { const response = await fetch(url, { ...options, signal: abortControllerRef.current.signal, }); const data = await response.json() as T; if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } setState({ data, loading: false, error: null, status: response.status, }); options.onSuccess?.(data); } catch (error) { // Negeer abort errors if (error instanceof Error && error.name === 'AbortError') { return; } const errorMessage = error instanceof Error ? error.message : 'Unknown error'; setState(prev => ({ ...prev, loading: false, error: errorMessage, status: error instanceof Response ? error.status : null, })); options.onError?.(errorMessage); } }, [url, options]); const abort = useCallback(() => { abortControllerRef.current?.abort(); }, []); useEffect(() => { if (options.immediate !== false && url) { fetchData(); } return () => { abortControllerRef.current?.abort(); }; }, [fetchData, options.immediate, url]); return { ...state, refetch: fetchData, abort, }; } ``` ## Gebruik in Componenten ```typescript // Types voor onze data interface User { id: number; name: string; email: string; } interface Post { id: number; title: string; body: string; } // Gebruik voor gebruikers data function UserProfile({ userId }: { userId: number }) { const { data: user, loading, error, refetch } = useFetch<User>( `https://jsonplaceholder.typicode.com/users/${userId}`, { onSuccess: (userData) => { console.log('User data loaded:', userData); }, onError: (errorMsg) => { console.error('Failed to load user:', errorMsg); }, } ); if (loading) return <div>Loading user...</div>; if (error) return <div>Error: {error}</div>; if (!user) return <div>No user found</div>; return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> <button onClick={refetch}>Reload</button> </div> ); } // Gebruik voor posts met POST request function CreatePost() { const [title, setTitle] = useState(''); const { data: post, loading, error, refetch } = useFetch<Post>( 'https://jsonplaceholder.typicode.com/posts', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ title, body: 'Content' }), immediate: false, // Start niet automatisch } ); const handleSubmit = () => { refetch(); }; return ( <div> <input value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Post title" /> <button onClick={handleSubmit} disabled={loading}> {loading ? 'Creating...' : 'Create Post'} </button> {error && <div>Error: {error}</div>} {post && <div>Post created: {post.title}</div>} </div> ); } ``` ## Best Practices voor TypeVeiligheid en Herbruikbaarheid ### 1. Generieke Types Correct Gebruiken ```typescript // Goed: Duidelijke type constraints interface ApiResponse<T> { data: T; meta: { page: number; total: number; }; } export function useApiFetch<T>( endpoint: string, options?: UseFetchOptions ) { // Type de response expliciet const { data, ...rest } = useFetch<ApiResponse<T>>( `https://api.example.com/${endpoint}`, options ); return { data: data?.data ?? null, meta: data?.meta ?? null, ...rest, }; } ``` ### 2. Hook Composability ```typescript // Specialized hooks gebouwd op useFetch export function useUsers() { return useFetch<User[]>('https://jsonplaceholder.typicode.com/users'); } export function useUser(userId: number) { return useFetch<User>( userId ? `https://jsonplaceholder.typicode.com/users/${userId}` : null ); } export function usePosts() { const { data, ...rest } = useFetch<Post[]>( 'https://jsonplaceholder.typicode.com/posts' ); // Transform data indien nodig const transformedData = useMemo(() => data?.map(post => ({ ...post, excerpt: post.body.substring(0, 100) + '...' })) ?? null , [data]); return { data: transformedData, ...rest }; } ``` ### 3. Error Boundaries en Status Management ```typescript // Uitgebreide error handling interface UseFetchWithRetryOptions extends UseFetchOptions { retries?: number; retryDelay?: number; } export function useFetchWithRetry<T>( url: string, options: UseFetchWithRetryOptions = {} ) { const [retryCount, setRetryCount] = useState(0); const { retries = 3, retryDelay = 1000, ...fetchOptions } = options; const { error, refetch, ...state } = useFetch<T>(url, fetchOptions); const retry = useCallback(async () => { if (retryCount < retries) { setTimeout(() => { setRetryCount(prev => prev + 1); refetch(); }, retryDelay); } }, [retryCount, retries, retryDelay, refetch]); return { ...state, error, refetch, retry, retryCount, canRetry: retryCount < retries && !!error, }; } ``` ### 4. Dependency Management ```typescript // Correct dependencies beheer export function useFetchWithDeps<T>( urlFactory: (deps: any[]) => string, deps: any[], options?: UseFetchOptions ) { const url = useMemo(() => urlFactory(deps), deps); return useFetch<T>(url, options); } // Gebruik: function UserPosts({ userId }: { userId: number }) { const { data: posts } = useFetchWithDeps<Post[]>( (deps) => `https://jsonplaceholder.typicode.com/users/${deps[0]}/posts`, [userId] // dependencies array ); // ... } ``` ## Belangrijkste Best Practices 1. **Type Safety**: Gebruik generieke types voor maximale typeveiligheid 2. **Dependency Arrays**: Zorg voor correcte dependency arrays in useCallback en useEffect 3. **Abort Controllers**: Implementeer request cancellation om memory leaks te voorkomen 4. **Error Handling**: Voorzie uitgebreide error handling met verschillende error types 5. **Composability**: Bouw gespecialiseerde hooks op basis van de basis hook 6. **Performance**: Gebruik useMemo en useCallback waar nodig 7. **Testing**: Maak hooks testbaar door dependencies te injecteren Deze aanpak zorgt voor herbruikbare, type-veilige hooks die effectief staat en bijwerkingen kunnen beheren in je React TypeScript applicaties.