PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

React.js – JSX, useState, useEffect, custom hooks, comparison with Vue

by Henryk Tews / Tuesday, 08 June 2021 / Published in JavaScript

After covering Vue.js in September, it is time for React – currently the most popular frontend library for building interactive UIs. React thinks differently from Vue: everything is a function, state flows one way, and JSX looks like HTML in JavaScript. I show the fundamentals from a PHP developer’s perspective and compare the two approaches.

React’s mental model vs Vue

Vue uses a declarative template syntax close to HTML – you can see the relationship to PHP template engines like Twig. React uses JSX – a syntax extension where you write what looks like HTML directly in JavaScript functions. It is a different mindset but becomes natural quickly.

// React component - a function that returns JSX
function ProductCard({ product, onAddToCart }) {
    // Everything is a JS function - no options object
    const [quantity, setQuantity] = React.useState(1);
    const totalPrice = (product.price * quantity).toFixed(2);

    return (
        // JSX - HTML-like syntax compiled to JavaScript
        <div className="product-card">
            <h3>{product.name}</h3>
            <p>{totalPrice} PLN</p>

            <div>
                <button onClick={() => setQuantity(q => Math.max(1, q - 1))}>-</button>
                <span>{quantity}</span>
                <button onClick={() => setQuantity(q => q + 1)}>+</button>
            </div>

            <button onClick={() => onAddToCart(product.id, quantity)}>
                Add to cart
            </button>
        </div>
    );
}

useState – managing local state

import { useState } from 'react';

function Counter() {
    // useState returns [currentValue, setter]
    // analogous to a PHP class property + setter
    const [count, setCount] = useState(0);
    const [name,  setName]  = useState('');

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={() => setCount(count + 1)}>Increment</button>
            <button onClick={() => setCount(0)}>Reset</button>

            <input
                value={name}
                onChange={e => setName(e.target.value)}
                placeholder="Your name"
            />
            <p>Hello, {name || 'stranger'}!</p>
        </div>
    );
}

useEffect – side effects and API calls

import { useState, useEffect } from 'react';

function ProductList({ categoryId }) {
    const [products, setProducts] = useState([]);
    const [loading,  setLoading]  = useState(false);
    const [error,    setError]    = useState(null);

    // useEffect runs after render
    // second argument [] means "run once on mount"
    // [categoryId] means "run when categoryId changes"
    useEffect(() => {
        setLoading(true);
        setError(null);

        fetch(`/rest/V1/products?searchCriteria[filterGroups][0][filters][0][field]=category_id&searchCriteria[filterGroups][0][filters][0][value]=${categoryId}`)
            .then(r => r.json())
            .then(data => {
                setProducts(data.items ?? []);
                setLoading(false);
            })
            .catch(err => {
                setError(err.message);
                setLoading(false);
            });

        // Cleanup function (optional) - runs before next effect or unmount
        return () => {
            // cancel fetch if component unmounts before response arrives
        };
    }, [categoryId]); // dependency array

    if (loading) return <p>Loading...</p>;
    if (error)   return <p>Error: {error}</p>;

    return (
        <ul>
            {products.map(p => (
                <li key={p.id}>{p.name} - {p.price} PLN</li>
            ))}
        </ul>
    );
}

Custom hooks – extracting reusable logic

// Custom hook - the PHP equivalent of a service class or trait
function useMagentoProduct(productId) {
    const [product,  setProduct]  = useState(null);
    const [loading,  setLoading]  = useState(false);
    const [error,    setError]    = useState(null);

    useEffect(() => {
        if (!productId) return;

        setLoading(true);
        fetch(`/rest/V1/products/${productId}`)
            .then(r => {
                if (!r.ok) throw new Error('Product not found');
                return r.json();
            })
            .then(data => { setProduct(data); setLoading(false); })
            .catch(err => { setError(err.message); setLoading(false); });
    }, [productId]);

    return { product, loading, error };
}

// Use the hook in any component
function ProductDetail({ productId }) {
    const { product, loading, error } = useMagentoProduct(productId);

    if (loading) return <div>Loading...</div>;
    if (error)   return <div>Error: {error}</div>;
    if (!product) return null;

    return (
        <div>
            <h1>{product.name}</h1>
            <p>SKU: {product.sku}</p>
            <p>Price: {product.price} PLN</p>
        </div>
    );
}

React vs Vue – quick comparison

Aspect React Vue 3
Syntax JSX (HTML in JS) Templates (HTML + directives)
Learning curve Steeper Gentler
State management hooks (useState, useReducer) ref, reactive, Pinia
Community size Larger Large
Magento ecosystem PWA Studio, Hyvä React Vue Storefront
TypeScript support Excellent Excellent

Summary

React’s functional approach with hooks is more powerful than Vue’s Options API but requires a different way of thinking. The key shift for a PHP developer: instead of mutating state directly, you always call the setter. Once you internalise that, React code becomes predictable and easy to follow. If you plan to work with Magento PWA Studio or Hyvä React – React knowledge is essential.

About Henryk Tews

What you can read next

Vue.js for the PHP developer – Options API, Composition API, Magento REST communication
TypeScript for the PHP developer – types, interfaces, generics, async/await
Next.js for the PHP developer – SSR, Server Components, GraphQL with Magento

© 2026 Created by

TOP
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Always active
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}