Sobes.tech
Junior

Jaké jsou hooky v Reactu?

sobes.tech AI

Odpověď od AI

V Reactu, od verze 16.8, hooky umožňují funkcionálním komponentám spravovat stav a používat životní cykly, které dříve byly dostupné pouze u třídových komponent.

Zde jsou hlavní vestavěné hooky:

  • useState: Umožňuje přidat vnitřní stav do funkčního komponentu. Vrací pár: aktuální stav a funkci pro jeho aktualizaci.

    // Příklad useState
    import React, { useState } from 'react';
    
    function Counter() {
      const [count, setCount] = useState(0); // Inicializace stavu na 0
    
      return (
        <div>
          <p>Stiskli jste {count} krát</p>
          <button onClick={() => setCount(count + 1)}>
            Klikni na mě
          </button>
        </div>
      );
    }
    
  • useEffect: Umožňuje provádět vedlejší efekty v funkčních komponentách. Podobné metodám životního cyklu třídových komponent (componentDidMount, componentDidUpdate, componentWillUnmount).

    // Příklad useEffect
    import React, { useState, useEffect } from 'react';
    
    function Example() {
      const [count, setCount] = useState(0);
    
      // Spouští se po každém renderu
      useEffect(() => {
        document.title = `Stiskli jste ${count} krát`;
      });
    
      // Spouští se pouze při prvním renderu (prázdné pole závislostí)
      useEffect(() => {
        console.log('Komponenta byla namontována');
        return () => {
          console.log('Komponenta byla odmontována'); // Čistící funkce
        };
      }, []);
    
      // Spouští se při změně count
      useEffect(() => {
        console.log('Count se změnil:', count);
      }, [count]);
    
      return (
        <div>
          <p>Stiskli jste {count} krát</p>
          <button onClick={() => setCount(count + 1)}>
            Klikni na mě
          </button>
        </div>
      );
    }
    
  • useContext: Umožňuje přihlásit se ke změnám kontextu React. Přijímá objekt kontextu vytvořený pomocí React.createContext().

    // Příklad useContext
    import React, { useContext } from 'react';
    
    const ThemeContext = React.createContext('light'); // Vytvoření kontextu
    
    function ThemedButton() {
      const theme = useContext(ThemeContext); // Použití kontextu
    
      return (
        <button className={theme}>
          Tlačítko v tématu {theme}
        </button>
      );
    }
    
    function App() {
      return (
        <ThemedButton />
      );
    }
    
  • useReducer: Alternativa k useState pro složitější správu stavu, který obsahuje více podhodnot nebo když závisí další stav na předchozím. Podobné reduktorům v Redux.

    // Příklad useReducer
    import React, { useReducer } from 'react';
    
    const initialState = { count: 0 };
    
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return { count: state.count + 1 };
        case 'decrement':
          return { count: state.count - 1 };
        default:
          throw new Error();
      }
    }
    
    function Counter() {
      const [state, dispatch] = useReducer(reducer, initialState);
    
      return (
        <>
          Count: {state.count}
          <button onClick={() => dispatch({ type: 'increment' })}>+</button>
          <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
        </>
      );
    }
    
  • useCallback: Memoizuje zpětné volání funkce. Vrací memoizovanou verzi callbacku, která se mění pouze při změně jedné ze závislostí. Užitečné pro zabránění zbytečnému přerenderování podkomponent při předávání callbacků.

    // Příklad useCallback
    import React, { useState, useCallback } from 'react';
    
    function Parent({ addTodo }) {
      const [count, setCount] = useState(0);
    
      const handleAddTodo = useCallback(() => {
        addTodo();
      }, [addTodo]); // závislost na addTodo
    
      return (
        <>
          <button onClick={() => setCount(count + 1)}>
            Zvýšit čítač
          </button>
          <Child onAddItem={handleAddTodo} />
        </>
      );
    }
    
    function Child({ onAddItem }) {
      console.log('Child se renderuje'); // Bude se renderovat pouze při změně onAddItem
      return <button onClick={onAddItem}>Přidat položku</button>;
    }
    
  • useMemo: Memoizuje vypočítanou hodnotu. Vrací memoizovanou hodnotu, která se přepočítává pouze při změně jedné ze závislostí. Užitečné pro výkon při náročných výpočtech.

    // Příklad useMemo
    import React, { useMemo, useState } from 'react';
    
    function expensiveCalculation(num) {
      console.log('Probíhá nákladný výpočet...');
      for (let i = 0; i < 1000000; i++) {
        num += 1;
      }
      return num;
    }
    
    function Example() {
      const [count, setCount] = useState(0);
      const [todos, setTodos] = useState([]);
    
      const calculation = useMemo(() => expensiveCalculation(count), [count]); // závislost na count
    
      const addTodo = () => {
        setTodos(t => [...t, "Nový úkol"]);
      };
    
      return (
        <div>
          <div>
            <h2>Moje úkoly</h2>
            {todos.map((todo, index) => (
              <p key={index}>{todo}</p>
            ))}
            <button onClick={addTodo}>Přidat úkol</button>
          </div>
          <hr />
          <div>
            Číslo: {count}
            <button onClick={() => setCount(c => c + 1)}>+</button>
            <h2>Nákladný výpočet</h2>
            {calculation}
          </div>
        </div>
      );
    }
    
  • useRef: Umožňuje vytvořit mutabilní odkaz, který přetrvává po celou dobu životnosti komponenty. Často se používá pro přístup k DOM elementům nebo uchovávání jakékoli mutabilní hodnoty, která nezpůsobí přerenderování při změně.

    // Příklad useRef
    import React, { useRef } from 'react';
    
    function TextInputWithFocusButton() {
      const inputEl = useRef(null); // vytvoření odkazu
    
      const onButtonClick = () => {
        inputEl.current.focus(); // zaměření na DOM element
      };
    
      return (
        <>
          <input ref={inputEl} type="text" /> // přiřazení odkazu
          <button onClick={onButtonClick}>Zaostřit na pole</button>
        </>
      );
    }
    
  • useImperativeHandle: Nastavuje descriptor instance, který je poskytován rodičovskému komponentu při použití ref. Umožňuje dětskému komponentu odhalit určité metody rodiči. Používá se společně s forwardRef.

    // Příklad useImperativeHandle
    import React, { useRef, useImperativeHandle, forwardRef } from 'react';
    
    const MyInput = forwardRef((props, ref) => {
      const inputRef = useRef();
    
      useImperativeHandle(ref, () => ({
        focusInput: () => {
          inputRef.current.focus();
        }
      }));
    
      return <input ref={inputRef} {...props} />;
    });
    
    function ParentComponent() {
      const inputRef = useRef();
    
      const handleFocus = () => {
        inputRef.current.focusInput(); // volání metody z dětského komponentu
      };
    
      return (
        <div>
          <MyInput ref={inputRef} />
          <button onClick={handleFocus}>Zaostřit přes Parent</button>
        </div>
      );
    }
    
  • useLayoutEffect: Stejný jako useEffect, ale běží synchronně po všech mutacích DOM. Užitečné pro čtení dat z DOM nebo provádění mutací, které musí být před vykreslením. Může blokovat renderování.

    // Příklad useLayoutEffect
    import React, { useLayoutEffect, useRef } from 'react';
    
    function Tooltip({ children, text }) {
      const ref = useRef(null);
    
      useLayoutEffect(() => {
        if (ref.current) {
          const { top, height } = ref.current.getBoundingClientRect();
          console.log(`Prvek je na ${top} s výškou ${height}`);
        }
      }, [children]);
    
      return (
        <span ref={ref} title={text}>
          {children}
        </span>
      );
    }
    
  • useDebugValue: Zobrazí uživatelskou značku pro hooky v React DevTools. Používá se v vlastních hookách pro ladění.

    // Příklad useDebugValue
    import React, { useState, useDebugValue } from 'react';
    
    function useFriendStatus(friendID) {
      const [isOnline, setIsOnline] = useState(null);
    
      // Zobrazí "Friend Status: Offline" nebo "Friend Status: Online" v DevTools
      useDebugValue(isOnline === true ? 'Online' : 'Offline');
    
      // ... logika získání stavu přítele
    
      return isOnline;
    }
    
    function FriendListItem({ friend }) {
      const isOnline = useFriendStatus(friend.id);
    
      return (
        <li style={{ color: isOnline ? 'zelená' : 'černá' }}>
          {friend.name} - {isOnline ? 'Online' : 'Offline'}
        </li>
      );
    }
    

Kromě vestavěných hooků existují také vlastní hooky (Custom Hooks). Jsou to JavaScriptové funkce, jejichž jména začínají na "use". Umožňují opakované použití logiky stavu mezi komponentami. Vlastní hook může volat jiné hooky.

// Příklad vlastního hooku
import { useState, useEffect } from 'react';

function useWindowSize() {
  const [size, setSize] = useState([window.innerWidth, window.innerHeight]);

  useEffect(() => {
    const handleResize = () => {
      setSize([window.innerWidth, window.innerHeight]);
    };
    window.addEventListener('resize', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return size;
}

function MyComponent() {
  const [width, height] = useWindowSize(); // použití vlastního hooku

  return (
    <p>Velikost okna: {width}x{height}</p>
  );
}

Důležité pravidla pro hooky:

  1. Volat hooky pouze na vrchní úrovni React komponenty nebo vlastního hooku. Nevolat je uvnitř cyklů, podmínek nebo vnořených funkcí.
  2. Volat hooky pouze z funkčních komponent React nebo vlastních hooků. Nevolat je z běžných JavaScriptových funkcí nebo metod třídních komponent.