React Хукови — шта су, основни хукови и правила коришћења

Аутор: IT Sectr Објављено: 2026-07-04 Време читања: 10 мин

React Хукови — то су функције које омогућавају коришћење стања, ефеката, контекста и других React могућности у функционалним компонентама, без писања класа. Представљени у React 16.8, хукови су радикално променили приступ развоју, заменивши сложене обрасце попут HOC и render props. Према React, 2024, постоји 15 уграђених хукова који покривају све сценарије управљања компонентом.

Главно

  • Хукови — функције за повезивање React могућности са функционалним компонентама без extends Component.
  • useState — основни хук за управљање стањем, враћа вредност и сеттер.
  • useEffect — за споредне ефекте: АПИ захтеви, претплате, тајмери, DOM манипулације.
  • useContext — за приступ React контексту без Consumer омотача и static contextType.
  • Правила хукова — позивати само на највишем нивоу и само унутар функционалних компоненти или прилагођених хукова.

Шта су React хукови?

React Хукови — скуп функција уграђених у React које омогућавају „повезивање” са унутрашњим механизмима оквира без креирања класа. Пре React 16.8, функционалне компоненте су могле само да примају props и враћају JSX — свака сложена логика је захтевала класну компоненту.

Проблем класних компоненти — раздвајање повезане логике. Код који се односи на једну целину (на пример, WebSocket претплата) био је принудно подељен између componentDidMount, componentDidUpdate и componentWillUnmount. Хукови то решавају: цела логика се групише у једном useEffect.

Други проблем — поновно коришћење логике. За поновно коришћење логике стања између класних компоненти морали су се примењивати HOC (компоненте вишег реда) или render props — оба обрасца су стварала додатно угњежђење у стаблу компоненти. Прилагођени хукови решавају овај проблем без угњежђења.

useState — управљање стањем

useState — најосновнији хук. Прима почетну вредност и враћа торку: тренутну вредност и функцију за њено ажурирање. За разлику од this.setState у класама, useState не спаја објекте — ново стање у потпуности замењује претходно за одређени позив useState.

js
import React, { useState } from 'react';

function Form() {
  const [text, setText] = useState('');
  const [submitting, setSubmitting] = useState(false);

  async function handleSubmit() {
    setSubmitting(true);
    await fetch('/api/submit', {
      method: 'POST',
      body: JSON.stringify({ text })
    });
    setSubmitting(false);
  }

  return (
    <div>
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <button onClick={handleSubmit}
        disabled={submitting}>
        {submitting ? 'Saving...' : 'Submit'}
      </button>
    </div>
  );
}

Функционална форма setState

Сеттер useState може примити функцију која добија претходну вредност и враћа нову. Функционална форма је обавезна када се ново стање израчунава из претходног и могући су бачевани позиви setState — сваки позив ће добити ажурну претходну вредност.

useEffect — споредни ефекти и животни циклус

useEffect — хук за извођење споредних ефеката у функционалним компонентама. Прима два аргумента: функцију-ефекат и низ зависности. React извршава ефекат после сваког рендеровања, али при навођењу зависности — само када се било која од њих промени.

Празан низ зависности [] значи да се ефекат извршава једном после првог рендеровања (аналогно componentDidMount). Враћање функције из ефекта је cleanup, који се извршава при демонтирању (аналогно componentWillUnmount) и пре поновног извршавања ефекта.

js
import React, { useState, useEffect } from 'react';

function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const socket = new WebSocket(`wss://chat/${roomId}`);

    socket.onmessage = (event) => {
      setMessages(prev => [...prev, JSON.parse(event.data)]);
    };

    // Cleanup - затвори сокет
    return () => socket.close();
  }, [roomId]);

  return <ul>
    {messages.map((msg, i) =>
      <li key={i}>{msg.text}</li>
    )}
  </ul>;
}

Зависности useEffect — једина су веза између ефекта и спољног света. Ако се у ефекту користи променљива из props или стања, она мора бити у низу зависности. Игнорисање овог правила је извор грешака са застарелим подацима (stale closure).

useContext — приступ контексту

useContext — хук за читање вредности контекста. Прима објекат контекста креиран путем React.createContext и враћа тренутну вредност. Ако се контекст промени, компонента се аутоматски поново рендерује са новом вредношћу.

Пре појаве useContext, приступ контексту у функционалним компонентама је захтевао омотач у Context.Consumer, што је стварало угњежђење. useContext потпуно елиминише Consumer — довољно је позвати хук на било ком месту функционалне компоненте.

js
import React, { createContext, useContext } from 'react';

const ThemeContext = createContext('светло');

function ThemedButton() {
  const theme = useContext(ThemeContext);

  return <button
    className={`btn btn-${theme}`}
  >
    {theme === 'dark' ? 'moon' : '☀️'}
  </button>;
}

useContext није замена за Redux или глобално стање. React препоручује коришћење контекста за ретко промењиве вредности — тема, језик, ауторизација. За често ажуриране податке, свако поновно рендеровање ће поново израчунати све потрошаче контекста, што може смањити перформансе.

useReducer — сложена логика стања

useReducer — алтернатива useState за стања са сложеном логиком ажурирања. Прима reducer функцију и почетно стање, враћа тренутно стање и dispatch функцију. Reducer прима тренутно стање и акцију, враћа ново стање — чиста функција без споредних ефеката.

useReducer је посебно користан када више акција (set, reset, increment, addItem) управља једним стањем. Уместо више useState са различитим сеттерима — један reducer са јасним типовима акција. То чини логику предвидљивом и лако тестираном.

js
import React, { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + 1 };
    case 'setStep':
      return { ...state, step: action.step };
    case 'reset':
      return { count: 0, step: 1 };
    default:
      throw new Error('Unknown action: ' + action.type);
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, {
    count: 0,
    step: 1
  });

  return (
    <>
      <p>Count: {state.count}</p>
      <button
        onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button
        onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </>
  );
}

useRef, useMemo, useCallback — оптимизација

useRef ствара променљиви објекат који се задржава између рендеровања. Промена current не изазива поновно рендеровање. Главна примена — приступ DOM елементима и чување мутабилних вредности (тајмери, претходне вредности, инстанце библиотека).

useMemo мемоизира резултат функције. Прерачунава вредност само када се промени једна од зависности. Користите за скупа израчунавања — филтрирање великих низова, сортирање, форматирање података. useCallback — посебан случај useMemo за мемоизацију функција.

js
import React, { useRef, useMemo, useCallback } from 'react';

function SearchPage({ items, query, onItemSelect }) {
  const inputRef = useRef(null);

  // Аутофокус при монтирању
  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  // Мемоизирај филтрирани резултат
  const filtered = useMemo(() => {
    return items.filter(item =>
      item.name.toLowerCase()
        .includes(query.toLowerCase())
    );
  }, [items, query]);

  // Мемоизирај callback
  const handleSelect = useCallback((id) => {
    onItemSelect(id);
  }, [onItemSelect]);

  return (
    <div>
      <input ref={inputRef}
        value={query}
        onChange={e => setQuery(e.target.value)}
      />
      <ul>
        {filtered.map(item =>
          <li key={item.id}>
            <button
              onClick={() => handleSelect(item.id)}>
              {item.name}
            </button>
          </li>
        )}
      </ul>
    </div>
  );
}

Важно правило: useRef, useMemo и useCallback — алати за оптимизацију, а не обавезне конструкције. Не обмотавајте све у useMemo — прво измерите перформансе. Преурана оптимизација компликује код. React рендерује компоненте брзо; useMemo је потребан само када су израчунавања заиста скупа (O(n²) и више).

Правила хукова и честе грешке

React је увео два строга правила за хукове. Прво: позивајте хукове само на највишем нивоу — не унутар услова, петљи или угњеждених функција. Друго: позивајте хукове само из React функционалних компоненти или прилагођених хукова. ESLint додатак eslint-plugin-react-hooks аутоматски проверава оба правила.

Најчешћа грешка — кршење редоследа позивања хукова. React се ослања на редослед позивања хукова између рендеровања. Ако се хук налази унутар услова и није позван у једном од рендеровања, сви наредни хукови се померају, изазивајући грешке.

ГрешкаШта се дешаваРешење
Хук у условуРедослед хукова се ломиПреместите логику услова унутар хука
Stale closureЕфекат користи стару вредностДодајте све зависности у низ
Бесконачна петљаuseEffect без зависности мења стањеНаведите зависности или користите useReducer
Измена ref у рендеруСпоредни ефекат у телу функцијеПреместите мутације у useEffect

Друга честа грешка — stale closure у useEffect. Ако се у ефекту користи променљива која није наведена у зависностима, ефекат „хвата” њену вредност у тренутку стварања затворења. Решење — увек укључите све коришћене променљиве у низ зависности. Ако има превише зависности, поделите ефекат на неколико.

Често постављана питања

Зашто се хукови не могу позивати у условима?

React се ослања на редослед позивања хукова између рендеровања. Ако је хук позван у услову и није радио у једном од рендеровања, сви наредни хукови „клизе” — њихово стање се меша. ESLint правило hooks/exhaustive-deps упозорава на ово.

Која је разлика између useEffect и useLayoutEffect?

useLayoutEffect је синхрони и извршава се пре него што прегледач нацрта измене. useEffect је асинхрони — после цртања. Користите useLayoutEffect само када треба измерити или изменити DOM пре него што корисник види измене (позиција popover-а, враћање скрола).

Како отказати fetch захтев при демонтирању?

Користите AbortController унутар useEffect. Направите controller, проследите signal у fetch, и у cleanup функцији позовите controller.abort(). За старе прегледаче довољна је заставица cancelled: return () => { cancelled = true; } са провером пре setState.

Шта је прилагођени хук и када га креирати?

Прилагођени хук — JavaScript функција са префиксом use која користи уграђене хукове. Креирајте га када се иста логика са хуковима понавља у две или више компоненти. Примери: useDebounce, useLocalStorage, useMediaQuery. Прилагођени хукови замењују HOC и render props.

Зашто се useEffect извршава двапут у Strict Mode-у?

У Strict Mode-у React намерно монтира и демонтира компоненту двапут у развојном режиму да би пронашао грешке са неочишћеним ефектима. Ако је cleanup исправно написан, двоструки позив не изазива проблеме. У продукционом режиму ово се не дешава.

Резиме

  • React Хукови — функције за коришћење стања, ефеката и контекста у функционалним компонентама, које су замениле класе.
  • useState — основни хук за локално стање са функционалним сеттером за безбедна ажурирања.
  • useEffect — универзални хук за споредне ефекте са низом зависности и cleanup функцијом.
  • useContext — директан приступ React контексту без Consumer омотача и додатног угњежђења.
  • useReducer — за сложену логику стања са типовима акција, алтернатива Redux-у у оквиру једне компоненте.
  • useRef, useMemo, useCallback — алати за оптимизацију: референце на DOM, мемоизација вредности и повратних позива.
  • Правило највишег нивоа — строго ограничење позивања хукова ван услова, петљи и угњеждених функција.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође