Complete Guide to React Hooks: useState, useEffect, and Custom Hooks - Blog yazısı ana kapak görseli
Tutorials

Complete Guide to React Hooks: useState, useEffect, and Custom Hooks

Learn how to use React Hooks effectively with practical examples and best practices for modern React development.

John Doe
4 min read
ReactHooksTutorialJavaScript

Complete Guide to React Hooks

React Hooks revolutionized how we write React components by allowing us to use state and other React features in functional components. In this comprehensive guide, we'll explore the most commonly used hooks and learn how to create custom hooks.

What are React Hooks?

Hooks are functions that let you "hook into" React state and lifecycle features from functional components. They were introduced in React 16.8 and have become the standard way to write React components.

useState Hook

The useState hook allows you to add state to functional components.

import React, { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Best Practices for useState

  1. Use multiple state variables for unrelated data
  2. Use functional updates when the new state depends on the previous state
  3. Initialize state lazily for expensive computations
// Functional update
setCount(prevCount => prevCount + 1);

// Lazy initialization
const [state, setState] = useState(() => {
  return expensiveComputation();
});

useEffect Hook

The useEffect hook lets you perform side effects in functional components.

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

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchUser() {
      setLoading(true);
      try {
        const response = await fetch(`/api/users/${userId}`);
        const userData = await response.json();
        setUser(userData);
      } catch (error) {
        console.error('Failed to fetch user:', error);
      } finally {
        setLoading(false);
      }
    }

    fetchUser();
  }, [userId]); // Dependency array

  if (loading) return <div>Loading...</div>;
  if (!user) return <div>User not found</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

useEffect Patterns

  1. Effect with cleanup:
useEffect(() => {
  const subscription = subscribeToSomething();
  
  return () => {
    subscription.unsubscribe();
  };
}, []);
  1. Conditional effects:
useEffect(() => {
  if (user) {
    document.title = `${user.name}'s Profile`;
  }
}, [user]);

Custom Hooks

Custom hooks allow you to extract component logic into reusable functions.

// Custom hook for API calls
function useApi(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      try {
        setLoading(true);
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    fetchData();
  }, [url]);

  return { data, loading, error };
}

// Using the custom hook
function UserList() {
  const { data: users, loading, error } = useApi('/api/users');

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

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

Other Useful Hooks

useContext

const theme = useContext(ThemeContext);

useReducer

const [state, dispatch] = useReducer(reducer, initialState);

useMemo and useCallback

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);

Best Practices

  1. Follow the Rules of Hooks:

    • Only call hooks at the top level
    • Only call hooks from React functions
  2. Use ESLint plugin: Install eslint-plugin-react-hooks to catch common mistakes

  3. Optimize performance: Use useMemo and useCallback judiciously

  4. Keep effects focused: Split unrelated logic into separate useEffect calls

Conclusion

React Hooks provide a powerful and flexible way to manage state and side effects in functional components. By mastering useState, useEffect, and custom hooks, you'll be able to write cleaner, more maintainable React code.

Remember to follow the rules of hooks and use the ESLint plugin to avoid common pitfalls. Happy coding!

Share Article

Author

John Doe

John Doe

Full-stack developer and technology enthusiast