30+ React Hooks Pytania Rekrutacyjne 2026: useEffect, useMemo, useCallback - Flipcards

30+ React Hooks Pytania Rekrutacyjne 2026: useEffect, useMemo, useCallback

Sławomir Plamowski 12 min czytania
Frontend JavaScript pytania-rekrutacyjne React React Hooks useCallback useEffect useMemo

"Kiedy NIE powinieneś używać useMemo?" - to pytanie pada na rozmowach rekrutacyjnych częściej niż mogłoby się wydawać. I większość kandydatów nie zna odpowiedzi. Bo dokumentacja React uczy JAK używać hooków, ale rzadko mówi KIEDY ich unikać. A właśnie to odróżnia juniora od seniora - świadomość, że każde narzędzie ma swój koszt.

Spis Treści

  1. Anti-Patterns w Rekrutacji Pytania
  2. useEffect Nadużywanie Pytania
  3. useMemo Memoizacja Pytania
  4. useCallback Optymalizacja Pytania
  5. Stale Closure Problem Pytania
  6. React.memo vs useMemo Pytania
  7. Rules of Hooks Pytania
  8. Pytania Rekrutacyjne z Odpowiedziami
  9. Powiązane Artykuły

Anti-Patterns w Rekrutacji Pytania

Pytania o anti-patterns hooków React to test dojrzałości programistycznej. Oto co rekruter sprawdza:

1. Rozumienie kosztów Każdy hook ma koszt:

  • useMemo - dodatkowa pamięć + porównywanie zależności
  • useCallback - dodatkowa alokacja + porównywanie
  • useEffect - dodatkowy cykl renderowania

2. Umiejętność profilowania Senior nie zgaduje - mierzy. React DevTools Profiler pokazuje rzeczywiste problemy.

3. Pragmatyzm Przedwczesna optymalizacja to korzeń wszelkiego zła. Lepszy czytelny kod niż "zoptymalizowany" bałagan.

graph TD A[Problem z wydajnością?] -->|Nie| B[Nie optymalizuj] A -->|Tak| C[Zmierz w Profilerze] C --> D{Wąskie gardło?} D -->|Rendering| E[Rozważ React.memo] D -->|Obliczenia| F[Rozważ useMemo] D -->|Callback w deps| G[Rozważ useCallback] B --> H[Pisz czytelny kod]

useEffect Nadużywanie Pytania

useEffect jest nadużywany do obliczeń, synchronizacji stanu i transformacji danych. Powinien być używany tylko do efektów ubocznych: fetch danych, subskrypcje, manipulacja DOM. Jeśli możesz coś obliczyć podczas renderowania - zrób to bez useEffect.

Kiedy NIE używać useEffect

1. Transformacja danych do renderowania

// ZŁE - niepotrzebny useEffect
function ProductList({products}) {
    const [filteredProducts, setFilteredProducts] = useState([]);

    useEffect(() => {
        setFilteredProducts(products.filter(p => p.inStock));
    }, [products]);

    return <List items={filteredProducts}/>;
}

// DOBRE - oblicz podczas renderowania
function ProductList({products}) {
    const filteredProducts = products.filter(p => p.inStock);
    return <List items={filteredProducts}/>;
}

2. Resetowanie stanu przy zmianie props

// ZŁE - useEffect do resetowania
function UserProfile({userId}) {
    const [user, setUser] = useState(null);

    useEffect(() => {
        setUser(null); // reset
        fetchUser(userId).then(setUser);
    }, [userId]);
}

// DOBRE - użyj key do wymuszenia remount
<UserProfile key={userId} userId={userId}/>

3. Inicjalizacja stanu z props

// ZŁE
function Counter({initialCount}) {
    const [count, setCount] = useState(0);

    useEffect(() => {
        setCount(initialCount);
    }, []);
}

// DOBRE - inicjalizacja w useState
function Counter({initialCount}) {
    const [count, setCount] = useState(initialCount);
}

Poprawne użycie useEffect

// Fetch danych - TO JEST OK
useEffect(() => {
    let cancelled = false;

    async function fetchData() {
        const response = await fetch(`/api/user/${userId}`);
        if (!cancelled) {
            setUser(await response.json());
        }
    }

    fetchData();

    return () => {
        cancelled = true;
    };
}, [userId]);

// Subskrypcja - TO JEST OK
useEffect(() => {
    const subscription = dataSource.subscribe(handleChange);
    return () => subscription.unsubscribe();
}, [dataSource]);

useMemo Memoizacja Pytania

useMemo szkodzi gdy: obliczenie jest proste (szybsze niż porównywanie zależności), zależności zmieniają się przy każdym renderowaniu, lub używasz go "na wszelki wypadek". Memoizacja ma koszt - pamięć i garbage collection.

useMemo zapamiętuje wynik obliczeń między renderowaniami. Ale ma koszt:

  1. Pamięć - przechowuje poprzedni wynik
  2. Porównywanie - przy każdym renderowaniu porównuje zależności
  3. GC pressure - więcej obiektów do sprzątnięcia

Kiedy NIE używać useMemo

1. Proste obliczenia

// ZŁE - koszt memoizacji > koszt obliczenia
const fullName = useMemo(
    () => `${firstName} ${lastName}`,
    [firstName, lastName]
);

// DOBRE - po prostu oblicz
const fullName = `${firstName} ${lastName}`;

2. Zależności zmieniające się zawsze

// ZŁE - nowy obiekt przy każdym renderowaniu
const config = useMemo(
    () => processConfig(options),
    [options] // options = {} każdorazowo = nowy obiekt
);

// Jeśli options tworzone inline, useMemo nic nie daje

3. Premature optimization

// ZŁE - "na wszelki wypadek"
const items = useMemo(
    () => data.map(item => <Item key={item.id} {...item} />),
    [data]
);

// Najpierw zmierz, potem optymalizuj

Kiedy useMemo MA sens

// Kosztowne obliczenia
const sortedData = useMemo(
    () => data.sort((a, b) => complexSort(a, b)),
    [data]
);

// Stabilna referencyjna tożsamość dla useEffect
const config = useMemo(
    () => ({threshold: 100, enabled: true}),
    [] // nigdy się nie zmienia
);

useEffect(() => {
    initializeWithConfig(config);
}, [config]); // teraz config ma stabilną referencję
flowchart TD A[Czy masz problem z wydajnością?] -->|Nie| B[Nie używaj useMemo] A -->|Tak| C[Czy obliczenie jest kosztowne?] C -->|< 1ms| B C -->|> 1ms| D[Czy zależności są stabilne?] D -->|Nie| E[Najpierw ustabilizuj zależności] D -->|Tak| F[Użyj useMemo]

useCallback Optymalizacja Pytania

useCallback zapobiega tworzeniu nowej funkcji przy każdym renderowaniu. Ale ma sens TYLKO gdy: przekazujesz funkcję do React.memo() komponentu, lub funkcja jest zależnością useEffect. W innych przypadkach to przedwczesna optymalizacja.

useCallback to useMemo dla funkcji:

// Te dwa zapisy są równoważne
const memoizedFn = useCallback(() => doSomething(a, b), [a, b]);
const memoizedFn = useMemo(() => () => doSomething(a, b), [a, b]);

Kiedy useCallback jest BEZUŻYTECZNY

1. Komponent dziecko nie jest memoizowany

// BEZUŻYTECZNE - Button nie jest React.memo
function Parent() {
    const handleClick = useCallback(() => {
        console.log('clicked');
    }, []);

    return <Button onClick={handleClick}/>;
}

// Button renderuje się przy każdym renderowaniu Parent
// czy handleClick ma nową referencję czy nie - bez znaczenia

2. Funkcja używana tylko lokalnie

// BEZUŻYTECZNE
function Form() {
    const validate = useCallback((value) => {
        return value.length > 0;
    }, []);

    // validate używane tylko tutaj, nie przekazywane dalej
    const handleSubmit = () => {
        if (validate(input)) { ...
        }
    };
}

Kiedy useCallback MA sens

1. Z React.memo()

const ExpensiveChild = React.memo(function ExpensiveChild({onClick}) {
    console.log('ExpensiveChild rendered');
    return <button onClick={onClick}>Click</button>;
});

function Parent() {
    // SENS - stabilizuje referencję dla memo komponentu
    const handleClick = useCallback(() => {
        console.log('clicked');
    }, []);

    return <ExpensiveChild onClick={handleClick}/>;
}

2. W zależnościach useEffect

function SearchComponent({query}) {
    const fetchResults = useCallback(async () => {
        const response = await fetch(`/api/search?q=${query}`);
        return response.json();
    }, [query]);

    useEffect(() => {
        fetchResults().then(setResults);
    }, [fetchResults]); // stabilna zależność
}

Stale Closure Problem Pytania

Stale closure to bug gdy useEffect lub useCallback używa nieaktualnych wartości z poprzedniego renderowania. Dzieje się tak gdy tablica zależności jest niekompletna. Rozwiązanie: zawsze dodawaj wszystkie używane zmienne do tablicy zależności.

JavaScript closures "zamykają" zmienne z momentu utworzenia funkcji. W React może to powodować problemy:

// BUG - stale closure
function Counter() {
    const [count, setCount] = useState(0);

    useEffect(() => {
        const id = setInterval(() => {
            console.log(count); // zawsze 0!
            setCount(count + 1); // zawsze 0 + 1 = 1
        }, 1000);

        return () => clearInterval(id);
    }, []); // pusta tablica - count "zamknięty" na 0
}

Dlaczego to się dzieje?

sequenceDiagram participant R as React participant E as useEffect participant I as setInterval R ->> E: Pierwszy render, count=0 E ->> I: Tworzy interval z count=0 R ->> R: setCount(1), count=1 Note over E, I: Interval nadal widzi count=0 I ->> I: console.log(0)

Rozwiązania

1. Dodaj zależność (może powodować reset intervalu)

useEffect(() => {
    const id = setInterval(() => {
        setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
}, [count]); // interval resetowany przy każdej zmianie

2. Użyj funkcyjnej formy setState

useEffect(() => {
    const id = setInterval(() => {
        setCount(prev => prev + 1); // nie zależy od count
    }, 1000);
    return () => clearInterval(id);
}, []); // bezpieczna pusta tablica

3. Użyj useRef dla aktualnej wartości

function Counter() {
    const [count, setCount] = useState(0);
    const countRef = useRef(count);

    useEffect(() => {
        countRef.current = count; // aktualizuj ref
    }, [count]);

    useEffect(() => {
        const id = setInterval(() => {
            console.log(countRef.current); // zawsze aktualna
        }, 1000);
        return () => clearInterval(id);
    }, []);
}

React.memo vs useMemo Pytania

React.memo() memoizuje komponent - zapobiega re-renderowaniu gdy props się nie zmieniły. useMemo memoizuje wartość - zapobiega ponownemu obliczaniu gdy zależności się nie zmieniły. React.memo działa na poziomie komponentu, useMemo na poziomie wartości.

To dwa różne narzędzia do różnych problemów:

Cecha React.memo() useMemo
Co memoizuje Cały komponent Wartość/obliczenie
Gdzie używamy Owijamy definicję komponentu Wewnątrz komponentu
Porównuje Props (shallow) Zależności (shallow)
Cel Pominięcie renderowania Pominięcie obliczenia

React.memo() w praktyce

// Bez memo - renderuje się przy każdym renderowaniu rodzica
function Tweet({author, content, likes}) {
    console.log('Tweet rendered');
    return (
        <div>
            <p>{content}</p>
            <span>By {author} | {likes} likes</span>
        </div>
    );
}

// Z memo - renderuje się tylko gdy props się zmienią
const Tweet = React.memo(function Tweet({author, content, likes}) {
    console.log('Tweet rendered');
    return (
        <div>
            <p>{content}</p>
            <span>By {author} | {likes} likes</span>
        </div>
    );
});

useMemo w praktyce

function TweetList({tweets, filter}) {
    // Bez useMemo - filtruje przy każdym renderowaniu
    const filteredTweets = tweets.filter(t => t.includes(filter));

    // Z useMemo - filtruje tylko gdy tweets lub filter się zmieni
    const filteredTweets = useMemo(
        () => tweets.filter(t => t.includes(filter)),
        [tweets, filter]
    );

    return filteredTweets.map(t => <Tweet key={t.id} {...t} />);
}

Kiedy używać czego?

flowchart TD A[Problem z wydajnością] --> B{Co jest wolne?} B -->|Komponent się re - renderuje| C[React.memo na komponencie] B -->|Obliczenie wewnątrz| D[useMemo na obliczeniu] C --> E{Props to funkcje?} E -->|Tak| F[Dodaj useCallback w rodzicu] E -->|Nie| G[React.memo wystarczy]

Rules of Hooks Pytania

Dwie zasady: 1) Wywołuj hooki tylko na najwyższym poziomie (nie w pętlach, warunkach, zagnieżdżonych funkcjach), 2) Wywołuj tylko w komponentach funkcyjnych lub własnych hookach. Te zasady pozwalają Reactowi poprawnie śledzić stan między renderowaniami.

React polega na kolejności wywołań hooków - musi być identyczna przy każdym renderowaniu.

Zasada 1: Tylko na najwyższym poziomie

// ZŁE - hook w warunku
function Form({isEditing}) {
    if (isEditing) {
        const [name, setName] = useState(''); // może nie zostać wywołany!
    }
    const [email, setEmail] = useState('');
}

// DOBRE - warunek wewnątrz hooka lub po hookach
function Form({isEditing}) {
    const [name, setName] = useState('');
    const [email, setEmail] = useState('');

    if (!isEditing) {
        return <p>View mode</p>;
    }
    // ...
}

Dlaczego kolejność ma znaczenie?

React identyfikuje hooki po pozycji, nie po nazwie:

// Pierwsze renderowanie:
useState('Mary')  // 1. Stan: 'Mary'
useEffect(...)    // 2. Efekt
useState('Poppins') // 3. Stan: 'Poppins'

// Drugie renderowanie (musi być identyczne):
useState('Mary')  // 1. Odczytaj stan: 'Mary'
useEffect(...)    // 2. Zamień efekt
useState('Poppins') // 3. Odczytaj stan: 'Poppins'

Jeśli hook jest w warunku, kolejność może się zmienić i React przypisze zły stan do złej zmiennej.

Zasada 2: Tylko w komponentach React lub własnych hookach

// ZŁE - hook w zwykłej funkcji
function validateEmail(email) {
    const [isValid, setIsValid] = useState(false); // error!
    // ...
}

// DOBRE - własny hook (nazwa zaczyna się od "use")
function useEmailValidation(email) {
    const [isValid, setIsValid] = useState(false);

    useEffect(() => {
        setIsValid(email.includes('@'));
    }, [email]);

    return isValid;
}

Pytania Rekrutacyjne z Odpowiedziami

Pytanie 1: "Opisz sytuację, w której useMemo pogorszy wydajność"

"useMemo pogorszy wydajność w trzech przypadkach:

  1. Proste obliczenia - porównywanie zależności jest droższe niż samo obliczenie
  2. Niestabilne zależności - gdy obiekt/tablica tworzona inline zmienia referencję przy każdym renderowaniu, useMemo i tak ponownie obliczy wartość, ale dodatkowo wykona porównanie
  3. Duże obiekty - memoizacja trzyma poprzednią wartość w pamięci, co może być problemem przy dużych strukturach danych

Najpierw mierzę w Profilerze, potem optymalizuję."

Pytanie 2: "Jak naprawiłbyś stale closure w useEffect?"

// Problem
useEffect(() => {
    const id = setInterval(() => {
        setCount(count + 1); // stale closure
    }, 1000);
    return () => clearInterval(id);
}, []);

// Rozwiązanie 1: Funkcyjna forma setState
useEffect(() => {
    const id = setInterval(() => {
        setCount(prev => prev + 1);
    }, 1000);
    return () => clearInterval(id);
}, []);

// Rozwiązanie 2: useRef dla aktualnej wartości
const countRef = useRef(count);
useEffect(() => {
    countRef.current = count;
}, [count]);

Pytanie 3: "Kiedy useCallback jest bezużyteczny?"

"useCallback jest bezużyteczny gdy:

  • Komponent przyjmujący funkcję nie jest opakowany w React.memo()
  • Funkcja nie jest przekazywana do dziecka, tylko używana lokalnie
  • Funkcja nie jest w tablicy zależności useEffect

W tych przypadkach to przedwczesna optymalizacja, która tylko komplikuje kod."

Pytanie 4: "Czym różni się przekazanie [] vs brak drugiego argumentu w useEffect?"

// Pusta tablica - uruchom RAZ przy montowaniu
useEffect(() => {
    console.log('mounted');
    return () => console.log('unmounted');
}, []);

// Brak argumentu - uruchom przy KAŻDYM renderowaniu
useEffect(() => {
    console.log('rendered');
});

"Pusta tablica to odpowiednik componentDidMount + componentWillUnmount. Brak argumentu to componentDidMount + componentDidUpdate + componentWillUnmount."

Pytanie 5: "Jak pominąć niepotrzebne wywołania useEffect?"

"Trzy sposoby:

  1. Tablica zależności - efekt uruchomi się tylko gdy zależności się zmienią
  2. Warunek wewnątrz efektu - sprawdź czy akcja jest potrzebna
  3. Właściwa granularność - rozbij na mniejsze efekty z różnymi zależnościami
// Zależności
useEffect(() => {
  document.title = `Clicked ${count} times!`;
}, [count]); // tylko gdy count się zmieni

// Warunek wewnątrz
useEffect(() => {
  if (user.id !== prevUserId) {
    fetchProfile(user.id);
  }
}, [user.id]);

Podsumowanie

React Hooks to potężne narzędzie, ale jak każde narzędzie - może być nadużywane. Kluczowe wnioski:

  1. useEffect - tylko do efektów ubocznych, nie do transformacji danych
  2. useMemo - tylko gdy masz zmierzone problemy z wydajnością
  3. useCallback - tylko z React.memo() lub w zależnościach
  4. Stale closures - używaj funkcyjnej formy setState lub useRef
  5. Rules of Hooks - hooki na najwyższym poziomie, zawsze w tej samej kolejności

Pamiętaj: przedwczesna optymalizacja to korzeń wszelkiego zła. Najpierw pisz czytelny kod, potem mierz, a dopiero potem optymalizuj tam, gdzie jest to naprawdę potrzebne.


Powiązane Artykuły


Sprawdź swoją wiedzę z React Hooks

Chcesz przećwiczyć więcej pytań o React Hooks przed rozmową rekrutacyjną?

Nasze fiszki React zawierają 50+ pytań o hooki, ich wzorce użycia i częste błędy. Każda fiszka ma szczegółową odpowiedź dostosowaną do poziomu Junior, Regular i Senior.

Zobacz fiszki React online - natychmiastowy dostęp, nauka w przeglądarce, bez instalacji.

Chcesz więcej pytań rekrutacyjnych?

To tylko jeden temat z naszego kompletnego przewodnika po rozmowach rekrutacyjnych. Uzyskaj dostęp do 800+ pytań z 13 technologii.

Kup pełny dostęp Zobacz bezpłatny podgląd
Powrót do blogu

Zostaw komentarz

Pamiętaj, że komentarze muszą zostać zatwierdzone przed ich opublikowaniem.