In this article we will discuss the core concepts like React JS state and lifecycle, which enable developers to create interactive, dynamic, and reusable components. In this tutorial, we will dive deep into understanding how React’s state and lifecycle work, along with practical examples to help you build real-world applications efficiently.
Understanding Props in React
What Are Props?
Props, short for properties, are inputs passed from one component to another, specifically from a parent to a child component. They allow data to flow down the component tree, ensuring that each component can access the data it needs.
Think of props as function arguments: they help pass information from one part of your app to another. However, unlike state, props are immutable.
What is State in React?
Defining State in React
State is a special object that allows React components to keep track of data that changes over time. Unlike props, which are read-only, the state can be updated, making it central to creating interactive components.
The state is managed within the component itself and can be modified using the setState
method or the useState
hook in functional components.
How State Works in React
State represents parts of the app that can change over time. Whenever state changes, React re-renders the component to update the UI accordingly. The setState()
function is used to update the state, and React takes care of reflecting those changes in the view.
class MyComponent extends React.Component {
constructor() {
super();
this.state = {
isVisible: false,
};
}
toggleVisibility = () => {
this.setState({ isVisible: !this.state.isVisible });
};
render() {
return (
<div>
<button onClick={this.toggleVisibility}>
{this.state.isVisible ? 'Hide' : 'Show'}
</button>
{this.state.isVisible && <p>Now you see me!</p>}
</div>
);
}
}
Managing State in Class Components
In class-based components, you initialize state inside the constructor. The constructor method is called when the component is created, and it sets up the initial state.
Example of Initializing State
constructor() {
super();
this.state = {
displayInfo: false,
};
}
This sets the initial state to false, meaning the information is hidden by default.
Functional Components vs Class Components
Functional Components Overview
Functional components are simpler to write and are stateless by default. They are functions that receive props as arguments and return JSX elements. With the introduction of React Hooks, functional components can now manage state and lifecycle methods.
Class Components Overview
Class components are more traditional in React and were the primary way to manage state and lifecycle events before hooks were introduced. Class components allow you to use the this
keyword and manage the component’s internal state.
State in Functional Components (React Hooks)
What is useState in React?
The useState
hook is a fundamental hook that lets you add state to functional components. It returns an array with two values: the current state and a function that allows you to update the state. The syntax is simple and straightforward, making state management much easier in functional components.
Using useState Hook
In functional components, you can use the useState
hook to manage local state. This hook returns two values: the current state and a function to update the state.
Example of useState in Functional Components
Here’s how to use the useState
hook in a functional component:
import React, { useState } from 'react';
function Hookstate() {
const [count, setCount] = useState(0);
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>Increase</button>
<button onClick={() => setCount(count - 1)}>Decrease</button>
</div>
);
}
Explanation:
- useState(0) initializes the state with a value of
0
. - count is the state variable that holds the current count value.
- setCount is the function used to update the state.
State in Class Components
Constructor and State Initialization
In class components, the state is typically initialized inside the constructor method. To use this keyword, you must call super() before initializing the state.
Understanding This Keyword in React
In class components, this refers to the component instance, allowing you to access the component’s props, state, and methods.
Example of State-in-Class Components
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state = {
message: 'Hello from Class Component!'
};
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
</div>
);
}
}
export default App;
React Lifecycle Methods
Introduction to Lifecycle Methods
Class components offer various lifecycle methods that allow you to execute code at specific times during a component’s life. Some common lifecycle methods are:
- componentDidMount: Executes after the component is mounted.
- componentDidUpdate: Runs when the component updates.
- componentWillUnmount: Runs before the component is removed from the DOM.
Example of React Lifecycle Methods in Class Component
import React, { Component } from 'react';
class LifecycleDemo extends Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
console.log('Constructor: Component is being initialized');
}
// Lifecycle method that runs after the component is added to the DOM
componentDidMount() {
console.log('componentDidMount: Component has been mounted');
// Simulating data fetching or a setup task
this.timer = setInterval(() => {
this.setState({ count: this.state.count + 1 });
}, 1000);
}
// Lifecycle method that runs after the component is updated
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate: Component has been updated');
if (prevState.count !== this.state.count) {
console.log(`Count updated to: ${this.state.count}`);
}
}
// Lifecycle method that runs just before the component is removed from the DOM
componentWillUnmount() {
console.log('componentWillUnmount: Component is being removed');
// Cleanup tasks, like clearing intervals or removing event listeners
clearInterval(this.timer);
}
render() {
return (
<div>
<h1>Lifecycle Demo</h1>
<p>Count: {this.state.count}</p>
</div>
);
}
}
export default LifecycleDemo;
Explanation of the Code:
- componentDidMount(): This method is called after the component is rendered and added to the DOM. In this example, it starts a timer that updates the state (
count
) every second. - componentDidUpdate(prevProps, prevState): This method is called after the component updates. It checks if the count has changed and logs the new count.
- componentWillUnmount(): This method is called just before the component is removed from the DOM. In the example, it clears the timer to prevent memory leaks.
Binding in React (in Class Components)
In JavaScript, the this
keyword behaves differently based on how a function is invoked. In React class components, when you create methods like readMore()
or showLess()
, they do not automatically have the correct context (this
) referring to the class instance. So, when you call them without binding, this
will be undefined
. To solve this, you can either use .bind()
or arrow functions.
Binding (.bind()
): In class components, methods like readMore
need to be bound to the class context in the constructor, so this
refers to the component instance.
Example: this.readMore = this.readMore.bind(this);
.
constructor() {
super();
// Initial state: displayinfo is set to false
this.state = {
displayinfo: false
};
console.log("React Project with Dinesh", this);
// Bind the method to ensure 'this' refers to the class instance
this.readMore = this.readMore.bind(this);
}
readMore() {
console.log("Read Info", this);
// Correctly updates the state to display the information
this.setState({ displayinfo: true });
}
render() {
return (
<div>
{/* Button to trigger readMore method */}
<button onClick={this.readMore}>Read more</button>
</div>
);
}
Explanation:
The constructor() is where we initialize the state and bind our methods.
this.readMore = this.readMore.bind(this) ensures that when readMore
is called, the this
inside the method points to the current class instance and can access this.state.
onClick={this.readMore}
will call the bound method correctly without errors.
Using Arrow Functions for Event Handlers in React
In React, arrow functions automatically bind the this
context. So, when using arrow functions, there’s no need to explicitly bind the method in the constructor.
Arrow Functions: Arrow functions automatically bind the correct this
context, so there’s no need for binding in the constructor.
Example: showLess = () => this.setState({ displayinfo: false })
Example with Arrow Function:
constructor() {
super();
// Initial state: displayinfo is set to false
this.state = {
displayinfo: false
};
}
// Using an arrow function automatically binds the method
showLess = () => {
this.setState({ displayinfo: false });
};
render() {
return (
<div>
{/* Using an arrow function to bind 'this' in the method */}
<button onClick={this.showLess}>Show Less</button>
</div>
);
}
Explanation:
- By declaring
showLess
as an arrow functionshowLess = () => {}
, we eliminate the need for binding in the constructor. - Arrow functions lexically bind
this
, meaning the function retains the context of the class it was declared in. onClick={this.showLess}
works without any errors sincethis
is already bound correctly.
Using toggleDisplayinfo() to Toggle State
You can also directly use arrow functions inside JSX, which creates an inline function that binds this
to the class context. However, this approach is less performant if used excessively because it creates a new function each time the component re-renders
Toggle Methods: You can create a method that toggles between states, useful for showing and hiding content.
Example: this.setState({ displayinfo: !this.state.displayinfo })
.
Arrow Functions in JSX: You can define arrow functions directly in the JSX for inline event handling, though this creates a new function on every re-render.
Example: <button onClick={() => this.toggleDisplayinfo()}>
.
constructor() {
super();
this.state = {
displayinfo: false
};
}
toggleDisplayinfo() {
this.setState({ displayinfo: !this.state.displayinfo });
}
render() {
return (
<div>
{/* Using arrow function directly in JSX */}
<button onClick={() => this.toggleDisplayinfo()}>
{this.state.displayinfo ? 'Show Less' : 'Read More'}
</button>
{this.state.displayinfo && <p>This is the detailed information.</p>}
</div>
);
}
Explanation:
- In this case, you do not need to bind the
toggleDisplayinfo()
method in the constructor, because the inline arrow function inonClick={() => this.toggleDisplayinfo()}
takes care of the binding. - While this is simple and avoids binding in the constructor, creating new functions on every render can slightly impact performance in large-scale applications.
Conclusion
Understanding the differences between props and state, as well as how to manage them in both functional and class components, is crucial to becoming proficient in React. React’s lifecycle methods also give you powerful control over component behavior at various stages.