State and Lifecycle

In React.js, state and lifecycle methods are fundamental concepts that allow you to create dynamic and interactive applications. Understanding how state works and how to manage component lifecycle events is crucial for building efficient and responsive user interfaces. In this chapter, we will explore state and lifecycle methods in detail, starting from the basics and gradually moving towards more advanced topics.

State in React

State is a built-in object in React components that holds data that can change over time. Unlike props, which are read-only and passed from parent to child components, state is managed within the component itself and can be modified through setState or the useState hook.

State in Class Components

In class components, state is initialized in the constructor and updated using the setState method.

				
					class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

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

export default Counter;

				
			

Explanation:

  • Counter is a class component.
  • this.state is initialized in the constructor with count set to 0.
  • The increment method updates the state using this.setState.
  • The render method displays the current count and a button to increment it.
				
					<div>
  <p>Count: 0</p>
  <button>Increment</button>
</div>

				
			

State in Functional Components

In functional components, state is managed using the useState hook.

				
					import React, { useState } from 'react';

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

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

export default Counter;

				
			

Explanation:

  • Counter is a functional component.
  • The useState hook initializes count to 0 and provides a function setCount to update it.
  • The component displays the current count and a button to increment it.
				
					<div>
  <p>Count: 0</p>
  <button>Increment</button>
</div>

				
			

Lifecycle Methods

Lifecycle methods are special methods in class components that are called at different stages of a component’s life. These methods allow you to run code at specific points in a component’s lifecycle.

Mounting

Mounting is the phase when a component is being created and inserted into the DOM.

  • constructor(): Called before the component is mounted. Used for initializing state and binding event handlers.
  • static getDerivedStateFromProps(): Called before rendering when new props or state are being received. Used to update the state in response to props changes.
  • render(): Required method that returns the React elements.
  • componentDidMount(): Called after the component is mounted. Ideal for making API calls and other side effects.
				
					class LifecycleDemo extends React.Component {
  constructor(props) {
    super(props);
    this.state = { data: null };
  }

  componentDidMount() {
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => this.setState({ data }));
  }

  render() {
    if (this.state.data === null) {
      return <div>Loading...</div>;
    }
    return <div>Data: {this.state.data}</div>;
  }
}

export default LifecycleDemo;

				
			

Explanation:

  • componentDidMount makes an API call after the component is mounted and updates the state with the fetched data.
				
					<div>Loading...</div>

<div>Data: [fetched data]</div>

				
			

Updating

Updating is the phase when a component is being re-rendered as a result of changes to props or state.

  • static getDerivedStateFromProps(): Called before rendering when new props or state are being received.
  • shouldComponentUpdate(): Called before rendering. Used to let React know if a component’s output is not affected by the current change in state or props.
  • render(): Re-renders the component.
  • getSnapshotBeforeUpdate(): Called right before the most recently rendered output is committed to the DOM. Used to capture some information from the DOM before it changes.
  • componentDidUpdate(): Called after the component is updated. Used for performing operations after the DOM has been updated.
				
					class UpdatingDemo extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  shouldComponentUpdate(nextProps, nextState) {
    return nextState.count % 2 === 0; // Re-render only if count is even
  }

  componentDidUpdate(prevProps, prevState) {
    console.log(`Updated from ${prevState.count} to ${this.state.count}`);
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

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

export default UpdatingDemo;

				
			

Explanation:

  • shouldComponentUpdate checks if the count is even before deciding to re-render.
  • componentDidUpdate logs the previous and current count after the component updates.
				
					<div>
  <p>Count: 0</p>
  <button>Increment</button>
</div>

				
			

Unmounting

Unmounting is the phase when a component is being removed from the DOM.

  • componentWillUnmount(): Called right before the component is unmounted and destroyed. Used for cleanup operations like clearing timers and canceling network requests.
				
					class Timer extends React.Component {
  constructor(props) {
    super(props);
    this.state = { seconds: 0 };
  }

  componentDidMount() {
    this.interval = setInterval(() => {
      this.setState({ seconds: this.state.seconds + 1 });
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    return <div>Seconds: {this.state.seconds}</div>;
  }
}

export default Timer;

				
			

Explanation:

  • componentWillUnmount clears the interval when the component is unmounted to prevent memory leaks.
				
					<div>Seconds: 0</div>

<div>Seconds: 1</div>

<div>Seconds: 2</div>


				
			

Advanced Topics

Using Hooks for Lifecycle Methods in Functional Components

React Hooks provide a way to use state and other React features in functional components, including lifecycle methods through hooks like useEffect.

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

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(seconds => seconds + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return <div>Seconds: {seconds}</div>;
}

export default Timer;

				
			

Explanation:

  • useEffect runs the provided effect after the component mounts.
  • The cleanup function inside useEffect clears the interval when the component unmounts.

Managing Complex State with useReducer

For complex state logic, useReducer can be more appropriate than useState.

				
					import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

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

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
}

export default Counter;

				
			

Explanation:

  • useReducer takes a reducer function and an initial state, returning the current state and a dispatch function.
  • The reducer function handles state transitions based on action types.
				
					<div>
  <p>Count: 0</p>
  <button>Increment</button>
  <button>Decrement</button>
</div>

				
			

Understanding state and lifecycle methods is crucial for building dynamic and interactive applications with React. Class components provide a clear structure for managing state and lifecycle events, while functional components with hooks offer a more modern and concise way to achieve the same goals. By mastering these concepts, you can create robust, efficient, and maintainable React applications.Happy coding !❤️

Table of Contents