TimoBlog
Back to all posts

Understanding React Hooks: A Practical Guide

Timothy Benjamin Timothy Benjamin
4 min read
Understanding React Hooks: A Practical Guide

Table of Contents

Share this post

Understanding React Hooks: A Practical Guide

React Hooks have revolutionized the way we write React components since their introduction in React 16.8. By allowing you to use state and other React features without writing a class, they’ve made functional components more powerful and intuitive.

Why Hooks Matter

Before Hooks, if you needed state in your component, you had to use a class component. This created an artificial divide in how developers approached component creation. With Hooks, that barrier disappeared, allowing developers to use a single, consistent pattern.

// Before Hooks (Class Component)
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.increment = this.increment.bind(this);
  }
  
  increment() {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

// With Hooks (Functional Component)
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

Essential Hooks to Master

1. useState

The useState Hook lets you add state to functional components. It returns a pair: the current state value and a function that lets you update it.

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

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

2. useEffect

The useEffect Hook lets you perform side effects in function components. It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in class components, but unified into a single API.

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

  // Similar to componentDidMount and componentDidUpdate
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
    
    // Clean up function (similar to componentWillUnmount)
    return () => {
      document.title = 'React App';
    };
  }, [count]); // Only re-run if count changes

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

3. useContext

The useContext Hook provides a way to pass data through the component tree without having to pass props down manually at every level.

const ThemeContext = React.createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return <ThemedButton />;
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>I am styled by theme context!</button>;
}

Custom Hooks: Reusing Logic

One of the most powerful features of Hooks is the ability to create custom Hooks that encapsulate reusable logic. This helps prevent duplicate code and makes your components cleaner.

Here’s an example of a custom Hook that manages a form input:

function useFormInput(initialValue) {
  const [value, setValue] = useState(initialValue);
  
  function handleChange(e) {
    setValue(e.target.value);
  }
  
  return {
    value,
    onChange: handleChange
  };
}

function ProfileForm() {
  const name = useFormInput('');
  const email = useFormInput('');
  
  function handleSubmit(e) {
    e.preventDefault();
    console.log('Submitted:', name.value, email.value);
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <input type="text" placeholder="Name" {...name} />
      <input type="email" placeholder="Email" {...email} />
      <button type="submit">Submit</button>
    </form>
  );
}

Best Practices for React Hooks

  1. Always use Hooks at the top level - Don’t call Hooks inside loops, conditions, or nested functions.

  2. Only call Hooks from React functions - Call Hooks from React function components or custom Hooks, not regular JavaScript functions.

  3. Name custom Hooks starting with “use” - This convention helps both humans and tools identify them as Hooks.

  4. Keep the dependency array accurate - For useEffect and other Hooks with dependencies, make sure to include all values from the component scope that change over time and are used inside the effect.

Conclusion

React Hooks have changed the way we think about and write React components, making them more intuitive, reusable, and easier to test. By embracing Hooks, you can write cleaner code that’s easier to maintain and understand.

As you continue your React journey, experiment with different Hooks and consider how they can simplify your component logic. Happy coding!

Timothy Benjamin

About Timothy Benjamin

A Freelance Full-Stack Developer who brings company website visions to reality.