Sobes.tech
Junior

Koji su hookovi u Reactu?

sobes.tech АИ

Одговор од АИ

У React-у, почев од верзије 16.8, хукови пружају функционалним компонентама могућност управљања стањем и коришћења животних циклуса, који су раније били доступни само класним компонентама.

Ево основних уграђених хукова:

  • useState: Омогућава додавање унутрашњег стања функционалној компоненти. Враћа пар: тренутно стање и функцију за његово ажурирање.

    // Пример useState
    import React, { useState } from 'react';
    
    function Counter() {
      const [count, setCount] = useState(0); // Инициализација стања са 0
    
      return (
        <div>
          <p>Притиснули сте {count} пута</p>
          <button onClick={() => setCount(count + 1)}>
            Притисни ме
          </button>
        </div>
      );
    }
    
  • useEffect: Омогућава извођење споредних ефеката у функционалним компонентама. Слично методама животног циклуса класних компоненти (componentDidMount, componentDidUpdate, componentWillUnmount).

    // Пример useEffect
    import React, { useState, useEffect } from 'react';
    
    function Example() {
      const [count, setCount] = useState(0);
    
      // Ради након сваког рендера
      useEffect(() => {
        document.title = `Притиснули сте ${count} пута`;
      });
    
      // Ради само при првом рендеру (празан низ зависности)
      useEffect(() => {
        console.log('Компонента је монтирана');
        return () => {
          console.log('Компонента је демонтирана'); // Функција за чишћење
        };
      }, []);
    
      // Ради при промени count
      useEffect(() => {
        console.log('Count се променио:', count);
      }, [count]);
    
      return (
        <div>
          <p>Притиснули сте {count} пута</p>
          <button onClick={() => setCount(count + 1)}>
            Притисни ме
          </button>
        </div>
      );
    }
    
  • useContext: Омогућава претплату на промене контекста React-а. Прихвата објекат контекста, креиран помоћу React.createContext().

    // Пример useContext
    import React, { useContext } from 'react';
    
    const ThemeContext = React.createContext('light'); // Креирање контекста
    
    function ThemedButton() {
      const theme = useContext(ThemeContext); // Коришћење контекста
    
      return (
        <button className={theme}>
          Копка у теми {theme}
        </button>
      );
    }
    
    function App() {
      return (
        <ThemedButton />
      );
    }
    
  • useReducer: Алтернатива useState за управљање сложенијим стањем, које укључује више подстандарда или када следеће стање зависи од претходног. Слично редујерима у Redux.

    // Пример 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 (
        <>
          Бројач: {state.count}
          <button onClick={() => dispatch({ type: 'increment' })}>+</button>
          <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
        </>
      );
    }
    
  • useCallback: Меморише функцију-повратни позив. Враћа меморизовану верзију колбека која се мења само при промени једне од зависности. Корисно за спречавање непотребних поновних рендера дечијих компоненти приликом преноса колбека.

    // Пример useCallback
    import React, { useState, useCallback } from 'react';
    
    function Parent({ addTodo }) {
      const [count, setCount] = useState(0);
    
      const handleAddTodo = useCallback(() => {
        addTodo();
      }, [addTodo]); // Зависност од addTodo
    
      return (
        <>
          <button onClick={() => setCount(count + 1)}>
            Увећај бројач
          </button>
          <Child onAddItem={handleAddTodo} />
        </>
      );
    }
    
    function Child({ onAddItem }) {
      console.log('Child се рендерује'); // Биће рендеровано само при промени onAddItem
      return <button onClick={onAddItem}>Додај елемент</button>;
    }
    
  • useMemo: Меморише израчунату вредност. Враћа меморизовану вредност која се поново израчунава само при промени једне од зависности. Корисно за перформансе при скупим израчунским операцијама.

    // Пример useMemo
    import React, { useMemo, useState } from 'react';
    
    function expensiveCalculation(num) {
      console.log('Извршава се скупа операција...');
      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]); // Зависност од count
    
      const addTodo = () => {
        setTodos(t => [...t, "Нови задатак"]);
      };
    
      return (
        <div>
          <div>
            <h2>Моји задатци</h2>
            {todos.map((todo, index) => (
              <p key={index}>{todo}</p>
            ))}
            <button onClick={addTodo}>Додај задатак</button>
          </div>
          <hr />
          <div>
            Бројач: {count}
            <button onClick={() => setCount(c => c + 1)}>+</button>
            <h2>Скупа операција</h2>
            {calculation}
          </div>
        </div>
      );
    }
    
  • useRef: Омогућава креирање мутабилног реф-а који се чува током целог животног века компоненти. Често се користи за приступ елементима DOM или чување било које мутабилне вредности која не изазива поновно рендеровање при промени.

    // Пример useRef
    import React, { useRef, useEffect } from 'react';
    
    function TextInputWithFocusButton() {
      const inputEl = useRef(null); // Креирање реф-а
    
      const onButtonClick = () => {
        // current указује на монтирани DOM елемент
        inputEl.current.focus();
      };
    
      return (
        <>
          <input ref={inputEl} type="text" /> // Привезивање реф-а
          <button onClick={onButtonClick}>Фокус на поље за унос</button>
        </>
      );
    }
    
  • useImperativeHandle: Подешава дескриптор инстанце који се пружа родитељском компоненту приликом коришћења ref. Омогућава дечијем компоненту да открије одређене методе родитељском компоненту. Користи се у сарадњи са forwardRef.

    // Пример 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(); // Позив метода из дечије компоненте
      };
    
      return (
        <div>
          <MyInput ref={inputRef} />
          <button onClick={handleFocus}>Фокус преко Parent-а</button>
        </div>
      );
    }
    
  • useLayoutEffect: Идентична је useEffect, али се извршава синхроно након свих мутација DOM-а. Полезна је за читање података из DOM-а или извођење мутација које је неопходно извршити пре него што прегледач поново приказује екран. Може блокирати рендеровање.

    // Пример useLayoutEffect
    import React, { useLayoutEffect, useRef } from 'react';
    
    function Tooltip({ children, text }) {
      const ref = useRef(null);
    
      useLayoutEffect(() => {
        if (ref.current) {
          // Израчунавања на основу DOM-а пре рендера
          const { top, height } = ref.current.getBoundingClientRect();
          console.log(`Елемент се налази на ${top} са висином ${height}`);
        }
      }, [children]);
    
      return (
        <span ref={ref} title={text}>
          {children}
        </span>
      );
    }
    
  • useDebugValue: Приказује корисничку етикету за хукове у React DevTools. Користи се у корисничким хуковима за дебаговање.

    // Пример useDebugValue
    import React, { useState, useDebugValue } from 'react';
    
    function useFriendStatus(friendID) {
      const [isOnline, setIsOnline] = useState(null);
    
      // Приказује 'Friend Status: Offline' или 'Friend Status: Online' у DevTools
      useDebugValue(isOnline === true ? 'Online' : 'Offline');
    
      // ... логика добијања статуса другара
    
      return isOnline;
    }
    
    function FriendListItem({ friend }) {
      const isOnline = useFriendStatus(friend.id);
    
      return (
        <li style={{ color: isOnline ? 'green' : 'black' }}>
          {friend.name} - {isOnline ? 'Онлајн' : 'Офлајн'}
        </li>
      );
    }
    

Поред уграђених хукова, постоје и кориснички хукови (Custom Hooks). То су JavaScript функције чија имена почињу са "use". Омогућавају поновну употребу логике стања између компоненти. Кориснички хуков може позивати друге хукове.

// Пример корисничког хука
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(); // Коришћење корисничког хука

  return (
    <p>Величина прозора: {width}x{height}</p>
  );
}

Важно је поштовати Правила хукова:

  1. Позивајте хукове само на врхунском нивоу React компоненти или корисничког хука. Не позивајте их унутар петљи, услова или унутрашњих функција.
  2. Позивајте хукове само из React функционалних компоненти или корисничких хукова. Не позивајте их из обичних JavaScript функција или метода класних компоненти.