• React Hooks Explained: useState and useEffect for Beginners

  • Learn React Hooks with practical examples. Master useState and useEffect to build modern, functional React components.

React Hooks Explained: useState and useEffect for Beginners

React Hooks Explained: useState and useEffect for Beginners

React Hooks revolutionized how we write React components. At SwedTech Academy, we teach you modern React practices from the start, focusing on functional components and hooks.


What Are React Hooks?

Hooks are functions that let you use state and other React features in functional components. Before hooks, you needed class components for state management. Now, functional components can do everything!


Why Use Hooks?

  • Simpler Code: Less boilerplate than classes
  • Better Code Reuse: Share logic between components easily
  • Easier Testing: Functional components are easier to test
  • Modern Standard: Industry best practice since 2019

useState: Managing Component State

useState is the most fundamental hook. It lets you add state to functional components.

Basic Syntax

import React, { useState } from 'react';

function Counter() {
  // Declare state variable
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

How useState Works

const [stateVariable, setStateVariable] = useState(initialValue);
  • stateVariable: Current state value
  • setStateVariable: Function to update state
  • initialValue: State's starting value

useState Examples

Simple State

function ToggleButton() {
  const [isOn, setIsOn] = useState(false);
  
  return (
    <button onClick={() => setIsOn(!isOn)}>
      {isOn ? 'ON' : 'OFF'}
    </button>
  );
}

Multiple State Variables

function UserForm() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [age, setAge] = useState(0);
  
  return (
    <form>
      <input 
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name"
      />
      <input 
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      <input 
        type="number"
        value={age}
        onChange={(e) => setAge(e.target.value)}
        placeholder="Age"
      />
    </form>
  );
}

Object State

function UserProfile() {
  const [user, setUser] = useState({
    name: '',
    email: '',
    age: 0
  });
  
  const updateUser = (field, value) => {
    setUser(prevUser => ({
      ...prevUser,
      [field]: value
    }));
  };
  
  return (
    <div>
      <input 
        value={user.name}
        onChange={(e) => updateUser('name', e.target.value)}
      />
      <input 
        value={user.email}
        onChange={(e) => updateUser('email', e.target.value)}
      />
    </div>
  );
}

useEffect: Side Effects in Components

useEffect lets you perform side effects in functional components. Side effects include:

  • Fetching data
  • Subscribing to services
  • Manually changing the DOM
  • Setting up timers

Basic Syntax

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

function Example() {
  const [count, setCount] = useState(0);
  
  // Runs after every render
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Click me
    </button>
  );
}

useEffect Dependency Array

Control when effects run using the dependency array:

// Runs after every render
useEffect(() => {
  console.log('After every render');
});

// Runs only once (on mount)
useEffect(() => {
  console.log('Only once');
}, []);

// Runs when dependencies change
useEffect(() => {
  console.log(`Count changed: ${count}`);
}, [count]);

// Multiple dependencies
useEffect(() => {
  console.log('Name or email changed');
}, [name, email]);

useEffect Examples

Fetching Data

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    fetch('https://api.example.com/users')
      .then(response => response.json())
      .then(data => {
        setUsers(data);
        setLoading(false);
      })
      .catch(error => {
        console.error('Error:', error);
        setLoading(false);
      });
  }, []); // Empty array = run once
  
  if (loading) return <p>Loading...</p>;
  
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

Cleanup Functions

function Timer() {
  const [seconds, setSeconds] = useState(0);
  
  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(s => s + 1);
    }, 1000);
    
    // Cleanup function
    return () => {
      clearInterval(interval);
    };
  }, []);
  
  return <div>Seconds: {seconds}</div>;
}

Event Listeners

function WindowSize() {
  const [width, setWidth] = useState(window.innerWidth);
  
  useEffect(() => {
    const handleResize = () => {
      setWidth(window.innerWidth);
    };
    
    window.addEventListener('resize', handleResize);
    
    // Cleanup
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);
  
  return <div>Window width: {width}px</div>;
}

Combining useState and useEffect

Real-world example: Search with debouncing

function SearchBar() {
  const [searchTerm, setSearchTerm] = useState('');
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(false);
  
  useEffect(() => {
    if (!searchTerm) {
      setResults([]);
      return;
    }
    
    setLoading(true);
    
    // Debounce: wait 500ms after user stops typing
    const timer = setTimeout(() => {
      fetch(`https://api.example.com/search?q=${searchTerm}`)
        .then(res => res.json())
        .then(data => {
          setResults(data);
          setLoading(false);
        });
    }, 500);
    
    return () => clearTimeout(timer);
  }, [searchTerm]);
  
  return (
    <div>
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="Search..."
      />
      {loading && <p>Loading...</p>}
      <ul>
        {results.map(result => (
          <li key={result.id}>{result.title}</li>
        ))}
      </ul>
    </div>
  );
}

Common Mistakes

1. Infinite Loops

// ❌ Wrong: Creates infinite loop
function BadExample() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    setCount(count + 1); // Updates count every render!
  });
  
  return <div>{count}</div>;
}

// ✅ Correct: Use dependency array
function GoodExample() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    console.log('Count changed:', count);
  }, [count]); // Only runs when count changes
  
  return <div>{count}</div>;
}

2. Forgetting Cleanup

// ❌ Memory leak!
useEffect(() => {
  setInterval(() => {
    console.log('Running...');
  }, 1000);
}, []);

// ✅ Proper cleanup
useEffect(() => {
  const interval = setInterval(() => {
    console.log('Running...');
  }, 1000);
  
  return () => clearInterval(interval);
}, []);

Best Practices

  1. One concern per effect: Separate effects for different purposes
  2. Always specify dependencies: Prevent bugs and optimize performance
  3. Use cleanup functions: Prevent memory leaks
  4. Keep effects simple: Complex logic should be extracted
  5. Use custom hooks: Reuse stateful logic across components

Custom Hooks Example

// Custom hook for fetching data
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(data => {
        setData(data);
        setLoading(false);
      })
      .catch(err => {
        setError(err);
        setLoading(false);
      });
  }, [url]);
  
  return { data, loading, error };
}

// Usage
function App() {
  const { data, loading, error } = useFetch('https://api.example.com/data');
  
  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;
  
  return <div>{JSON.stringify(data)}</div>;
}

Master React at SwedTech Academy

React is one of the most in-demand skills for frontend developers. In our Frontend Development and Full-Stack Development courses, you'll:

  • Build real applications with React
  • Master all essential hooks
  • Learn state management (Context API, Redux)
  • Create reusable custom hooks
  • Deploy production-ready apps

Ready to become a React expert? Enroll at SwedTech Academy and build your future!

Keywords: React Hooks, useState, useEffect, React tutorial, learn React, frontend development, SwedTech Academy
Want to know more?