Sobes.tech
Junior

Kokie yra hook'ai React?

sobes.tech AI

Atsakymas iš AI

Nuo React 16.8 versijos, hook'ai suteikia funkciniams komponentams galimybę valdyti būseną ir naudoti gyvenimo ciklus, kurie anksčiau buvo prieinami tik klasių komponentams.

Štai pagrindiniai įmontuoti hook'ai:

  • useState: leidžia pridėti vidinę būseną funkciniam komponentui. Grąžina porą: esamą būseną ir funkciją ją atnaujinti.

    // `useState` pavyzdys
    import React, { useState } from 'react';
    
    function Counter() {
      const [count, setCount] = useState(0); // Būsenos inicializavimas su 0
    
      return (
        <div>
          <p>Paspaudėte {count} kartus</p>
          <button onClick={() => setCount(count + 1)}>
            Spauskite mane
          </button>
        </div>
      );
    }
    
  • useEffect: leidžia vykdyti šalutinius efektus funkciniuose komponentuose. Panašus į klasių gyvenimo ciklo metodus (componentDidMount, componentDidUpdate, componentWillUnmount).

    // `useEffect` pavyzdys
    import React, { useState, useEffect } from 'react';
    
    function Example() {
      const [count, setCount] = useState(0);
    
      // vykdomas po kiekvieno atvaizdavimo
      useEffect(() => {
        document.title = `Paspaudėte ${count} kartus`;
      });
    
      // vykdomas tik pirmojo atvaizdavimo metu (tuščias priklausomybių masyvas)
      useEffect(() => {
        console.log('Komponentas įmontuotas');
        return () => {
          console.log('Komponentas išmontuotas'); // valymo funkcija
        };
      }, []);
    
      // vykdomas kai keičiasi count
      useEffect(() => {
        console.log('Count pasikeitė:', count);
      }, [count]);
    
      return (
        <div>
          <p>Paspaudėte {count} kartus</p>
          <button onClick={() => setCount(count + 1)}>
            Spauskite mane
          </button>
        </div>
      );
    }
    
  • useContext: leidžia prenumeruoti React konteksto pokyčius. Priima konteksto objektą, sukurtą naudojant React.createContext().

    // `useContext` pavyzdys
    import React, { useContext } from 'react';
    
    const ThemeContext = React.createContext('light'); // Konteksto sukūrimas
    
    function ThemedButton() {
      const theme = useContext(ThemeContext); // Konteksto naudojimas
    
      return (
        <button className={theme}>
          Mygtukas temoje {theme}
        </button>
      );
    }
    
    function App() {
      return (
        <ThemedButton />
      );
    }
    
  • useReducer: alternatyva useState sudėtingesnei būsenos valdymui, apimančiai kelis subreikinius ar kai sekantis būsenos pokytis priklauso nuo ankstesnio. Panašus į Redux reducerius.

    // `useReducer` pavyzdys
    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: memoizuoja atgalinio iškvietimo funkciją. Grąžina memoizuotą funkcijos versiją, kuri keičiasi tik pasikeitus vienai iš priklausomybių. Naudinga išvengti nereikalingo vaikų komponentų perperdavimo perduodant funkcijas.

    // `useCallback` pavyzdys
    import React, { useState, useCallback } from 'react';
    
    function Parent({ addTodo }) {
      const [count, setCount] = useState(0);
    
      const handleAddTodo = useCallback(() => {
        addTodo();
      }, [addTodo]); // priklausomybė nuo addTodo
    
      return (
        <>
          <button onClick={() => setCount(count + 1)}>
            Padidinti skaičių
          </button>
          <Child onAddItem={handleAddTodo} />
        </>
      );
    }
    
    function Child({ onAddItem }) {
      console.log('Vaiko komponentas atvaizduojamas'); // bus atvaizduojamas tik pasikeitus onAddItem
      return <button onClick={onAddItem}>Pridėti elementą</button>;
    }
    
  • useMemo: memoizuoja skaičiuojamą reikšmę. Grąžina memoizuotą reikšmę, kuri perperdaugia tik pasikeitus vienai iš priklausomybių. Naudinga našumui didelėms skaičiavimams.

    // `useMemo` pavyzdys
    import React, { useMemo, useState } from 'react';
    
    function expensiveCalculation(num) {
      console.log('Vykdomas brangus skaičiavimas...');
      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]); // priklausomybė nuo count
    
      const addTodo = () => {
        setTodos(t => [...t, "Naujas užrašas"]);
      };
    
      return (
        <div>
          <div>
            <h2>Mano užrašai</h2>
            {todos.map((todo, index) => (
              <p key={index}>{todo}</p>
            ))}
            <button onClick={addTodo}>Pridėti užrašą</button>
          </div>
          <hr />
          <div>
            Skaičius: {count}
            <button onClick={() => setCount(c => c + 1)}>+</button>
            <h2>Brangus skaičiavimas</h2>
            {calculation}
          </div>
        </div>
      );
    }
    
  • useRef: leidžia sukurti keičiamą nuorodą, kuri išlieka viso komponento gyvavimo ciklo metu. Dažnai naudojama prieigos prie DOM elementų ar saugojimo bet kokios keičiamo vertės, kuri ne sukelia perperdavimų.

    // `useRef` pavyzdys
    import React, { useRef } from 'react';
    
    function TextInputWithFocusButton() {
      const inputEl = useRef(null); // nuorodos sukūrimas
    
      const onButtonClick = () => {
        // current nurodo į įmontuotą DOM elementą
        inputEl.current.focus();
      };
    
      return (
        <>
          <input ref={inputEl} type="text" /> // nuorodos priskyrimas
          <button onClick={onButtonClick}>Fokusuoti į įvesties lauką</button>
        </>
      );
    }
    
  • useImperativeHandle: konfigūruoja egzemplioriaus aprašą, kuris pateikiamas tėviniam komponentui naudojant ref. Leidžia vaikų komponentui atskleisti tam tikras metodus tėviniam komponentui. Naudojama kartu su forwardRef.

    // `useImperativeHandle` pavyzdys
    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(); // iš vaiko komponento iškviečiama metodo funkcija
      };
    
      return (
        <div>
          <MyInput ref={inputRef} />
          <button onClick={handleFocus}>Fokusuoti per tėvą</button>
        </div>
      );
    }
    
  • useLayoutEffect: identiškas useEffect, bet vykdomas sinchroniškai po visų DOM mutacijų. Naudinga skaityti duomenis iš DOM arba atlikti mutacijas, kurios turi būti padarytos prieš ekrano atnaujinimą. Gali blokuoti atvaizdavimą.

    // `useLayoutEffect` pavyzdys
    import React, { useLayoutEffect, useRef } from 'react';
    
    function Tooltip({ children, text }) {
      const ref = useRef(null);
    
      useLayoutEffect(() => {
        if (ref.current) {
          // skaičiavimai remiantis DOM prieš atvaizdavimą
          const { top, height } = ref.current.getBoundingClientRect();
          console.log(`Elementas yra ${top} pozicijoje, aukštis ${height}`);
        }
      }, [children]);
    
      return (
        <span ref={ref} title={text}>
          {children}
        </span>
      );
    }
    
  • useDebugValue: rodo vartotojo žymę React DevTools. Naudojama kuriant savo hook'us, skirtus derinimui.

    // `useDebugValue` pavyzdys
    import React, { useState, useDebugValue } from 'react';
    
    function useFriendStatus(friendID) {
      const [isOnline, setIsOnline] = useState(null);
    
      // rodo "Friend Status: Offline" arba "Friend Status: Online" DevTools
      useDebugValue(isOnline === true ? 'Online' : 'Offline');
    
      // ... logika, kaip gauti draugo būseną
    
      return isOnline;
    }
    
    function FriendListItem({ friend }) {
      const isOnline = useFriendStatus(friend.id);
    
      return (
        <li style={{ color: isOnline ? 'green' : 'black' }}>
          {friend.name} - {isOnline ? 'Online' : 'Offline'}
        </li>
      );
    }
    

Be įmontuotų hook'ų, yra ir vartotojo sukurti hook'ai (Custom Hooks). Tai JavaScript funkcijos, kurių pavadinimai prasideda nuo "use". Jie leidžia pakartotinai naudoti būsenos logiką tarp komponentų. Vartotojo sukurtas hook'as gali kviesti kitus hook'us.

// Vartotojo sukurto hook'o pavyzdys
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(); // naudojamas vartotojo hook'as

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

Svarbu laikytis hook'ų taisyklių:

  1. Naudokite hook'us tik aukšto lygio funkcijų komponentuose arba vartotojo hook'uose. Nenaudokite jų cikluose, sąlygose ar vidinėse funkcijose.
  2. Naudokite hook'us tik iš React funkcinių komponentų arba vartotojo hook'ų. Nenaudokite jų įprastose JavaScript funkcijose ar klasių metodų viduje.