English flagEnglish

React cheat sheet – useState, useEffect, Props, Context, Redux

2026-04-17 5 perc olvasási idő

Bevezetés

Ez a cheat sheet összefoglalja a React fejlesztés legfontosabb mintáit TypeScript-tel. Minden egység önállóan is értelmezhető, de a sorrend logikus: az egyszerű lokális állapottól haladunk a globális állapotkezelés felé.

TémakörMire való
useStateLokális állapot egy komponensben
useEffectMellékhatások kezelése (fetch, timer, feliratkozás)
Kontrollált bevitelBeviteli mező értékének React-ben tartása
PropsAdatok és callback-ek átadása szülő → gyermek irányban
TypeScript interfész (interface)Props típusbiztos leírása
React ContextGlobális állapot prop drilling nélkül
Redux ToolkitSkálázható globális állapot nagy alkalmazásokhoz

1. useState – lokális állapotkezelés

A useState hook egy értéket tárol a komponensben, és minden változáskor újrarendereli azt.

Alap szintaxis

const [value, setValue] = useState(initialValue);

A változó neve és a setter neve szabad – az egyezmény szerint a setter neve set + a változó neve.

Számláló példa

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState<number>(0);

  return (
    <div>
      <p>Jelenlegi érték: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(count - 1)}>-1</button>
      <button onClick={() => setCount(0)}>Visszaállítás</button>
    </div>
  );
}

Boolean állapot (pl. megjelenítés ki/be)

function CollapsiblePanel() {
  const [isOpen, setIsOpen] = useState<boolean>(false);

  return (
    <div>
      <button onClick={() => setIsOpen(!isOpen)}>
        {isOpen ? 'Bezárás' : 'Megnyitás'}
      </button>
      {isOpen && <p>Ez a tartalom csak nyitott állapotban látható.</p>}
    </div>
  );
}

Objektum állapot

Ha több összefüggő értéket szeretnénk egyben tárolni, objektumot is használhatunk.

interface User {
  name: string;
  age: number;
}

function UserProfile() {
  const [user, setUser] = useState<User>({
    name: 'Kovács Péter',
    age: 21,
  });

  function handleBirthday() {
    // A spread operátorral minden korábbi mezőt megőrzünk
    setUser({ ...user, age: user.age + 1 });
  }

  return (
    <div>
      <p>{user.name}{user.age} éves</p>
      <button onClick={handleBirthday}>Születésnap!</button>
    </div>
  );
}
⚠️

Az állapotot sosem módosítsuk közvetlenül (pl. user.age = 22). Mindig a setter függvényt (setUser) hívjuk, különben a React nem fog újrarenderelni.


2. useEffect – mellékhatások

A useEffect hook olyan kódot futtat, amelynek a renderelés után kell végrehajtódnia: adatlekérés, feliratkozás, időzítők, DOM-manipuláció.

Szintaxis

useEffect(() => {
  // itt fut a mellékhatás

  return () => {
    // opcionális cleanup – a komponens lecsatolásakor fut
  };
}, [dependencies]);
Második paraméterMikor fut az effect?
Nincs megadvaMinden render után
[] (üres tömb)Csak az első render után (egyszer)
[a, b]Az első render után, majd ha a vagy b megváltozik

Adatlekérés az API-ról

import { useState, useEffect } from 'react';

interface Product {
  id: number;
  title: string;
  price: number;
}

function ProductList() {
  const [products, setProducts] = useState<Product[]>([]);
  const [isLoading, setIsLoading] = useState<boolean>(true);

  useEffect(() => {
    fetch('https://fakestoreapi.com/products?limit=5')
      .then((res) => res.json())
      .then((data: Product[]) => {
        setProducts(data);
        setIsLoading(false);
      });
  }, []); // üres tömb → csak egyszer fut le

  if (isLoading) return <p>Betöltés...</p>;

  return (
    <ul>
      {products.map((product) => (
        <li key={product.id}>
          {product.title}{product.price} USD
        </li>
      ))}
    </ul>
  );
}

Cleanup – takarítás a komponens lecsatolásakor

function TimerExample() {
  const [seconds, setSeconds] = useState<number>(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setSeconds((prev) => prev + 1);
    }, 1000);

    // cleanup: ha a komponens eltűnik, az időzítőt le kell állítani
    return () => clearInterval(intervalId);
  }, []);

  return <p>Eltelt idő: {seconds} másodperc</p>;
}
💡

Ha egy useEffect-en belül async függvényt szeretnél használni, definiáld azt a useEffect-en belül, majd azonnal hívd meg — ne tegyd magát az effect callback-et async-ká.

useEffect(() => {
  async function fetchData() {
    const res = await fetch('/api/data');
    const json = await res.json();
    setData(json);
  }
  fetchData();
}, []);

3. Kontrollált beviteli mezők

Kontrollált beviteli mező esetén a mező értékét React állapotban tároljuk. Az onChange esemény minden gombnyomáskor frissíti az állapotot, az value prop pedig visszaadja azt a mezőnek.

Egy mező

import { useState } from 'react';

function NameInput() {
  const [name, setName] = useState<string>('');

  return (
    <div>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Add meg a neved"
      />
      <p>Üdvözlünk, {name || 'ismeretlen'}!</p>
    </div>
  );
}

Több mező egy objektumban

Nagy formok esetén érdemes az összes mező értékét egyetlen objektumban tárolni, és egy generikus handler-t írni.

import { useState, ChangeEvent } from 'react';

interface RegistrationFormData {
  name: string;
  email: string;
  password: string;
}

function RegistrationForm() {
  const [formData, setFormData] = useState<RegistrationFormData>({
    name: '',
    email: '',
    password: '',
  });

  function handleChange(e: ChangeEvent<HTMLInputElement>) {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  }

  function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    console.log('Beküldött adatok:', formData);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="name"     value={formData.name}     onChange={handleChange} placeholder="Teljes név" />
      <input name="email"    value={formData.email}    onChange={handleChange} placeholder="E-mail cím" type="email" />
      <input name="password" value={formData.password} onChange={handleChange} placeholder="Jelszó"     type="password" />
      <button type="submit">Regisztráció</button>
    </form>
  );
}

A [name]: value szintaxis (dinamikus kulcs) lehetővé teszi, hogy egyetlen handler kezelje az összes beviteli mezőt. Ehhez az input-ok name attribútumának pontosan egyeznie kell az objektum kulcsával.

ℹ️

Az e.preventDefault() megakadályozza az alapértelmezett böngészőviselkedést, azaz az oldal újratöltését form beküldésekor.


4. Props – adatok átadása komponensek között

A props (tulajdonságok) a szülő komponenstől a gyermek felé irányuló adatáramlás eszközei. Mindig felülről lefelé áramlanak.

Egyszerű példa

// Gyermek komponens
function WelcomeMessage({ name }: { name: string }) {
  return <h2>Szia, {name}!</h2>;
}

// Szülő komponens
function App() {
  return <WelcomeMessage name="Anna" />;
}

Callback függvény átadása

Gyermek komponensek nem módosíthatják a szülő állapotát közvetlenül – helyette a szülő átad egy függvényt propként, amelyet a gyermek meghívhat.

interface ActionButtonProps {
  label: string;
  onClick: () => void;
}

function ActionButton({ label, onClick }: ActionButtonProps) {
  return <button onClick={onClick}>{label}</button>;
}

function App() {
  function handleClick() {
    alert('Megnyomták a gombot!');
  }

  return <ActionButton label="Kattints ide" onClick={handleClick} />;
}

children prop

interface CardProps {
  title: string;
  children: React.ReactNode;
}

function Card({ title, children }: CardProps) {
  return (
    <div className="card">
      <h3>{title}</h3>
      <div>{children}</div>
    </div>
  );
}

// Használat
<Card title="Fontos információ">
  <p>Ez a tartalom a children prop-on keresztül érkezik.</p>
</Card>

5. TypeScript interfészek props-hoz

Az interface kulcsszóval pontosan meghatározhatjuk, hogy egy komponens milyen prop-okat vár, milyen típussal, és melyek kötelezők.

Interfész definiálása

interface ProductCardProps {
  id: number;
  name: string;
  price: number;
  imageUrl?: string;           // opcionális (? jel)
  onAddToCart: (id: number) => void;
}

Interface extends – örököltetés

Ha két interfész között kód-ismétlést szeretnénk elkerülni:

interface BaseProduct {
  id: number;
  name: string;
  price: number;
}

interface FeaturedProductProps extends BaseProduct {
  discountPercent: number; // csak a kiemeltnél van
}

Teljes komponens interfésszel

import { useState } from 'react';

interface ProductCardProps {
  id: number;
  name: string;
  price: number;
  imageUrl?: string;
  onAddToCart: (id: number) => void;
}

function ProductCard({ id, name, price, imageUrl, onAddToCart }: ProductCardProps) {
  const [isInCart, setIsInCart] = useState<boolean>(false);

  function handleAddToCart() {
    onAddToCart(id);
    setIsInCart(true);
  }

  return (
    <div>
      {imageUrl && <img src={imageUrl} alt={name} />}
      <h3>{name}</h3>
      <p>{price} Ft</p>
      <button onClick={handleAddToCart} disabled={isInCart}>
        {isInCart ? 'Kosárban van' : 'Kosárba'}
      </button>
    </div>
  );
}
💡

Az interfészt érdemes különálló types/ mappában tárolni (types/index.ts), ha több komponens is használja, így elkerüljük a kód ismétlést.


6. React Context – globális állapot prop drilling nélkül

Ha egy állapotot mélyen egymásba ágyazott komponensek is használnak, a prop drilling (prop-ok végigadogatása szintről szintre) átláthatatlan lesz. A Context lehetővé teszi, hogy az állapotot bármelyik leszármazott komponens közvetlenül elérhesse.

Mikor használj Context-et?

  • Témakezelés (dark/light mód)
  • Bejelentkezett felhasználó adata
  • Nyelvi beállítások
  • Kis és közepes méretű alkalmazások globális állapota

A három lépés

  1. LétrehozáscreateContext
  2. BiztosításProvider becsomagolja a fa egy részét
  3. FogyasztásuseContext az értéket elolvassa

Teljes példa – kosár kontextus

// contexts/CartContext.tsx
import { createContext, useContext, useState, ReactNode } from 'react';

interface CartItem {
  id: number;
  name: string;
  price: number;
  quantity: number;
}

interface CartContextType {
  items: CartItem[];
  addToCart: (item: Omit<CartItem, 'quantity'>) => void;
  clearCart: () => void;
}

const CartContext = createContext<CartContextType | null>(null);

export function CartProvider({ children }: { children: ReactNode }) {
  const [items, setItems] = useState<CartItem[]>([]);

  function addToCart(newItem: Omit<CartItem, 'quantity'>) {
    setItems((prev) => {
      const existing = prev.find((item) => item.id === newItem.id);
      if (existing) {
        return prev.map((item) =>
          item.id === newItem.id ? { ...item, quantity: item.quantity + 1 } : item
        );
      }
      return [...prev, { ...newItem, quantity: 1 }];
    });
  }

  function clearCart() {
    setItems([]);
  }

  return (
    <CartContext.Provider value={{ items, addToCart, clearCart }}>
      {children}
    </CartContext.Provider>
  );
}

// Egyéni hook – szebb és biztonságosabb, mint a közvetlen useContext
export function useCart(): CartContextType {
  const ctx = useContext(CartContext);
  if (!ctx) {
    throw new Error('useCart csak CartProvider-en belül használható!');
  }
  return ctx;
}
// main.tsx
import { CartProvider } from './contexts/CartContext';

<CartProvider>
  <App />
</CartProvider>
// Bármely mélységű komponensben
import { useCart } from '../contexts/CartContext';

function ProductPage() {
  const { addToCart } = useCart();

  return (
    <button onClick={() => addToCart({ id: 1, name: 'Cipő', price: 12000 })}>
      Kosárba
    </button>
  );
}

function CartSummary() {
  const { items, clearCart } = useCart();

  const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);

  return (
    <div>
      <p>Összesen: {total} Ft</p>
      <button onClick={clearCart}>Kosár ürítése</button>
    </div>
  );
}
ℹ️

A Context nem csereli le a props-okat, csak a prop drilling problémáját oldja meg. Ha az állapot egyetlen szinten van és egy-két gyermeknek kell, props elegendő.


7. Redux Toolkit – skálázható globális állapotkezelés

A Redux Toolkit (RTK) a Redux modern, ajánlott verziója. Jelentősen kevesebb boilerplate-tel dolgozik, TypeScript-barát, és egységes mintát kínál.

Mikor érdemes Redux-ot választani Context helyett?

ContextRedux Toolkit
Kis és közepes alkalmazásNagy, összetett alkalmazás
Ritka frissítésekSűrű állapotmódosítások
Egyszerű logikaÖsszetett reducer-ek, több szelet
Nincs szükség dev tools-raRedux DevTools integráció

Telepítés

npm install @reduxjs/toolkit react-redux

Fájlstruktúra

src/
├── store/
│   ├── store.ts          # Konfiguráció és típusok
│   ├── hooks.ts          # Típusos hook-ok
│   └── counterSlice.ts   # Egy szelet (slice) = egy funkció állapota
└── main.tsx              # Provider

1. lépés – Szelet (slice) létrehozása

A slice tartalmazza az állapot kezdőértékét és az összes reducer-t (módosító függvényt) egy helyen.

// store/counterSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface CounterState {
  value: number;
}

const initialState: CounterState = {
  value: 0,
};

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      state.value += 1;  // Immer miatt közvetlenül módosítható!
    },
    decrement: (state) => {
      state.value -= 1;
    },
    incrementByAmount: (state, action: PayloadAction<number>) => {
      state.value += action.payload;
    },
    reset: (state) => {
      state.value = 0;
    },
  },
});

// Action creator-ök exportálása – ezeket dispatch-eljük majd
export const { increment, decrement, incrementByAmount, reset } = counterSlice.actions;

// A reducer exportálása a store-ba
export default counterSlice.reducer;
ℹ️

A Redux Toolkit az Immer könyvtárat használja a háttérben, ezért a reducer-eken belül úgy tűnik, mintha közvetlenül módosítanánk az állapotot (state.value += 1). Valójában Immer egy új, megváltoztatlan objektumot hoz létre – nem sértjük meg az immutabilitás elvét.

2. lépés – Store konfigurálása

// store/store.ts
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
    // ide kerülnek a többi szelet reducer-ei is
  },
});

// Típusok a TypeScript-hez – ezeket exportálni kell
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

3. lépés – Típusos hook-ok

A useSelector és useDispatch alapértelmezés szerint nem ismerik a store típusát. Egyszer kell létrehozni a típusos változataikat:

// store/hooks.ts
import { useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';

// Ezeket használd useSelector és useDispatch helyett a komponensekben!
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector = useSelector.withTypes<RootState>();

4. lépés – Provider beágyazása

// main.tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './store/store';
import App from './App';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </StrictMode>
);

5. lépés – Használat komponensekben

// components/Counter.tsx
import { useAppDispatch, useAppSelector } from '../store/hooks';
import { increment, decrement, incrementByAmount, reset } from '../store/counterSlice';

function Counter() {
  const value = useAppSelector((state) => state.counter.value);
  const dispatch = useAppDispatch();

  return (
    <div>
      <h2>Számlált érték: {value}</h2>
      <button onClick={() => dispatch(increment())}>+1</button>
      <button onClick={() => dispatch(decrement())}>-1</button>
      <button onClick={() => dispatch(incrementByAmount(10))}>+10</button>
      <button onClick={() => dispatch(reset())}>Nullázás</button>
    </div>
  );
}

export default Counter;

A useAppSelector kiolvassa az értéket a store-ból, a useAppDispatch + dispatch(action()) pedig módosítja azt.

Több szelet – pl. felhasználó is

// store/userSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface UserState {
  name: string | null;
  isLoggedIn: boolean;
}

const initialState: UserState = {
  name: null,
  isLoggedIn: false,
};

export const userSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {
    login: (state, action: PayloadAction<string>) => {
      state.name = action.payload;
      state.isLoggedIn = true;
    },
    logout: (state) => {
      state.name = null;
      state.isLoggedIn = false;
    },
  },
});

export const { login, logout } = userSlice.actions;
export default userSlice.reducer;
// store/store.ts – bővítve
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
import userReducer from './userSlice';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
    user: userReducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// Komponensben
const name = useAppSelector((state) => state.user.name);
const isLoggedIn = useAppSelector((state) => state.user.isLoggedIn);
💡

A Redux DevTools böngészőbővítménnyel (Chrome/Firefox) valós időben láthatod az állapot minden változását, az action előzményeket, és visszatekerhetsz korábbi állapotokhoz. Fejlesztés közben nélkülözhetetlen eszköz.


Összefoglalás – mikor mit?

IgényMegoldás
Egyetlen komponens saját állapotauseState
API-hívás, timer, eseményfeliratkozásuseEffect
<input> értékének React-ben tartásaKontrollált mező (value + onChange)
Adat átadása szülőtől gyermeknekProps
Prop-ok típusbiztos leírásaTypeScript interface
Megosztott állapot kis alkalmazásbanReact Context + useContext
Megosztott állapot nagy alkalmazásbanRedux Toolkit (createSlice, useAppSelector, useAppDispatch)