· 13 min read
React intro in 5 minutes
ReactJS is a powerful JavaScript library that is primarily used for building user interfaces (UIs) for web applications. It provides developers with a set of tools and abstractions to create interactive and dynamic UI components.
React is a popular JavaScript library for building user interfaces, and it revolves around several major concepts. Here are the key concepts in React:
- Components: React organizes UI elements into reusable building blocks called components. Components encapsulate their own logic, state, and rendering, making it easier to manage and maintain complex UIs.
- JSX (JavaScript XML): JSX is a syntax extension for JavaScript that allows you to write HTML-like code within JavaScript. It enables you to define the structure and appearance of components using a familiar markup-like syntax.
- Virtual DOM: React implements a virtual representation of the DOM (Document Object Model) known as the Virtual DOM. It’s a lightweight copy of the actual DOM and is used for efficient rendering and reconciliation of component changes.
- State and Props: State represents the internal data of a component, which can change over time. Props (short for properties) are inputs passed to a component from its parent. By managing state and props, you control the behavior and appearance of components.
- Reconciliation: When changes occur in a React application, React efficiently updates only the necessary parts of the UI using a process called reconciliation. It compares the previous Virtual DOM with the new one, identifying the differences and applying the minimal set of changes to the actual DOM.
- Lifecycle Methods: React components have a lifecycle consisting of various phases such as mounting, updating, and unmounting. Lifecycle methods allow you to perform actions at specific points in a component’s lifecycle, such as initializing state, fetching data, or cleaning up resources.
- React Router: React Router is a popular library for handling routing in React applications. It allows you to define different routes and their corresponding components, enabling navigation and rendering of different views based on the current URL.
- Context API: The Context API provides a way to share data between components without explicitly passing props at each level. It allows you to create a global state accessible to multiple components, simplifying state management in larger applications.
- React Hooks and Effects: React hooks like useState, useEffect, useContext, and many others provide a declarative way to add functionality and side effects to functional components. They enable you to handle component state, perform asynchronous operations, subscribe to updates, and more.
Let’s cut to the chase: React Hooks
React provides several built-in hooks that allow you to add state and other React features to functional components. Here are some of the most important hooks in React:
- useState: useState is used to add local state to functional components. It takes an initial state value and returns an array with two elements: the current state value and a function to update that state value. You can use this hook to manage and update state within a functional component.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
- useEffect: useEffect allows you to perform side effects in functional components, such as data fetching, subscriptions, or manipulating the DOM. It takes a function as its first argument and runs that function after rendering and whenever specified dependencies change.
import React, { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
// Fetch data from an API
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
}, []);
return <div>{data ? <p>Data: {data}</p> : <p>Loading...</p>}</div>;
}
- useContext: useContext enables you to access and consume values from a React context within a functional component. It takes a context object created by React.createContext and returns the current context value.
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
function ThemedText() {
const theme = useContext(ThemeContext);
return <p>Current theme: {theme}</p>;
}
- useRef: useRef returns a mutable ref object that can be used to hold a value across component renders. It’s useful for accessing and manipulating DOM elements, creating timers, or storing any mutable value that persists between renders.
import React, { useRef } from 'react';
function TextInput() {
const inputRef = useRef();
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}
- useCallback: The useCallback hook is used to memoize functions, preventing unnecessary re-renders of components that depend on those functions. It returns a memoized version of the callback function, which only changes if one of the dependencies specified in the dependency array changes.
import React, { useCallback, useState } from 'react';
function Button({ onClick }) {
return <button onClick={onClick}>Click me</button>;
}
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<Button onClick={handleClick} />
<p>Count: {count}</p>
</div>
);
}
In the above example, the handleClick function is memoized using useCallback with the count dependency. It ensures that the handleClick function remains the same unless the count changes, preventing unnecessary re-renders of the Button component.
- useMemo: The useMemo hook is used to memoize expensive computations, so they are only recalculated when the dependencies change. It takes a function and a dependency array, and returns the memoized result of the function.
import React, { useMemo, useState } from 'react';
function ExpensiveCalculation({ value }) {
const result = useMemo(() => {
// Perform expensive calculation
let sum = 0;
for (let i = 1; i <= value; i++) {
sum += i;
}
return sum;
}, [value]);
return <p>Result: {result}</p>;
}
function App() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<button onClick={increment}>Increment</button>
<ExpensiveCalculation value={count} />
</div>
);
}
In the above example, the ExpensiveCalculation component uses useMemo to memoize the result of the expensive calculation. The result is only recalculated when the value dependency (count) changes.
- useReducer: The useReducer hook is an alternative to useState for managing complex state logic. It takes a reducer function and an initial state, and returns the current state and a dispatch function to update the state.
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('Invalid action');
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
const increment = () => {
dispatch({ type: 'increment' });
};
const decrement = () => {
dispatch({ type: 'decrement' });
};
return (
<div>
<p>Count: {state.count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
React life cycle methods vs React hooks
React lifecycle methods and React hooks are two different approaches to managing component behavior and state in React. Here’s a comparison between the two:
React Lifecycle Methods:
-
Lifecycle methods are methods that are invoked at specific stages of a component’s life cycle, such as mounting, updating, and unmounting.
-
Class components traditionally used lifecycle methods like
componentDidMount
,componentDidUpdate
, andcomponentWillUnmount
to handle side effects, manage state, and interact with the DOM. -
Lifecycle methods are powerful but can make code harder to understand and maintain, especially when dealing with complex component hierarchies or managing shared state.
React Hooks:
-
Hooks are functions provided by React that allow functional components to have state and handle lifecycle events without the need for class components.
-
Hooks, such as
useState
,useEffect
,useContext
, provide a more declarative and composable way to manage state and side effects within functional components. -
Hooks allow you to extract and reuse logic, making it easier to reason about and test your code. They also promote a more functional programming style by decoupling behavior from the component hierarchy.
-
Hooks are designed to work seamlessly with functional components and enable functional components to have the same capabilities as class components.
Advantages of React Hooks:
-
Simplicity and Readability: Hooks reduce the cognitive load by simplifying the component structure and logic. They allow you to keep related code together, making it easier to understand and maintain.
-
Reusability: Hooks promote code reuse and modularity. You can extract and reuse custom hooks, encapsulating specific behavior or state management, and share them across multiple components.
-
Improved Performance: Hooks enable more efficient updates and re-rendering of components by allowing fine-grained control over when and how effects and state updates occur.
-
Avoidance of Class-related Issues: Hooks eliminate the need to deal with the complexities of class components, such as
this
binding, constructor, and lifecycle method intricacies.
However, it’s important to note that lifecycle methods are still supported in React and have their use cases, especially in legacy codebases or when integrating with third-party libraries that rely on class components.
React hooks provide a more modern and concise way to handle component behavior and state management, and they are the recommended approach for new React projects. They offer greater flexibility, simplicity, and reusability while minimizing the complexity associated with class components and lifecycle methods.
Example code in life cycle components vs react hooks
Here’s an example code that demonstrates the equivalent functionality using class components with lifecycle methods and functional components with React hooks.
Example: Class Components with Lifecycle Methods
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
componentDidMount() {
console.log('Component mounted');
}
componentDidUpdate() {
console.log('Component updated');
}
componentWillUnmount() {
console.log('Component will unmount');
}
increment = () => {
this.setState(prevState => ({
count: prevState.count + 1,
}));
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
export default Counter;
Example: Functional Components with React Hooks
import React, { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Component mounted');
return () => {
console.log('Component will unmount');
};
}, []);
useEffect(() => {
console.log('Component updated');
});
const increment = () => {
setCount(prevCount => prevCount + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
export default Counter;
In the above examples, both the class component and functional component achieve the same functionality of rendering a counter and incrementing its value. However, the class component uses lifecycle methods (componentDidMount
, componentDidUpdate
, componentWillUnmount
) to handle mounting, updating, and unmounting behavior.
On the other hand, the functional component uses the useState
hook to manage state and the useEffect
hook to handle the equivalent lifecycle behavior. The useEffect
hook with an empty dependency array acts as a replacement for componentDidMount
and componentWillUnmount
. The useEffect
hook without a dependency array is equivalent to componentDidUpdate
as it runs on every update.
Both approaches achieve the same result, but functional components with hooks offer a more concise and expressive way of managing component behavior, avoiding class-related complexities and promoting reusability of custom hooks.
Are react classes dead?
React classes are not dead, but functional components with React hooks have become the recommended approach for new React projects. React hooks provide a more modern and concise way to handle component behavior and state management, and they offer several advantages over class components. However, existing codebases that use class components are still fully supported and will continue to work without any issues.
Here are a few reasons why functional components with hooks have gained popularity:
-
Simplicity and Readability: Functional components with hooks reduce the boilerplate code and provide a more straightforward and readable syntax. The code becomes more concise, making it easier to understand and maintain.
-
Reusability: Hooks promote code reuse by allowing you to extract and reuse custom hooks across multiple components. Custom hooks encapsulate specific behavior or state management, making it easier to share and reuse that logic.
-
Improved Performance: Hooks allow for more fine-grained control over when and how effects and state updates occur. This can lead to more efficient updates and re-rendering of components, resulting in improved performance.
-
Functional Programming Paradigm: Functional components with hooks align well with functional programming principles, which promotes immutability, composability, and a focus on pure functions. This approach can lead to more predictable and easier-to-test code.
While functional components with hooks have become the recommended approach, it’s important to note that class components are still supported and will continue to work in React. Existing codebases and projects using class components can still be maintained and updated without any issues.
So, while functional components with hooks have gained significant popularity and offer numerous benefits, it doesn’t mean that class components are no longer relevant or supported. The choice between functional components with hooks and class components ultimately depends on the specific needs and requirements of your project.
A note on React vs Angular
React and Angular are two popular JavaScript frameworks used for building web applications. Here’s a comparison of React and Angular based on several factors:
- Learning Curve:
-
React: React has a relatively lower learning curve compared to Angular. It has a simpler API and focuses on component-based development.
-
Angular: Angular has a steeper learning curve due to its extensive feature set and complex concepts like dependency injection, decorators, and TypeScript.
- Architecture:
-
React: React is a library focused on the view layer of the application. It allows you to build UI components and manage their state efficiently. However, you need to choose additional libraries and tools for other aspects like routing and state management.
-
Angular: Angular is a full-fledged framework that provides a complete solution for building large-scale applications. It offers a built-in router, dependency injection, state management, and other features out of the box.
- Language:
-
React: React uses JavaScript (ES6+), which is a widely adopted and flexible language. It also supports JSX, an extension that allows embedding HTML-like syntax within JavaScript code.
-
Angular: Angular uses TypeScript, a superset of JavaScript that adds static typing and additional features to the language. TypeScript provides better tooling, code maintainability, and scalability.
- Community and Ecosystem:
-
React: React has a large and active community with a vast ecosystem of libraries and tools. There are numerous open-source projects, UI component libraries (e.g., Material-UI), and a strong focus on reusable components.
-
Angular: Angular also has a strong community and ecosystem with official support from Google. It provides a comprehensive set of tools, libraries (e.g., Angular Material), and official documentation. Angular’s focus on convention and structure can make it easier to find resources and guidance.
- Performance:
-
React: React’s virtual DOM efficiently updates and renders only the necessary components, resulting in good performance. However, the overall performance heavily depends on the implementation and the libraries used alongside React.
-
Angular: Angular’s change detection mechanism and Ahead-of-Time (AOT) compilation provide good performance out of the box. Angular’s structure and strict guidelines can help optimize performance.
- Mobile Development:
-
React: React Native, a framework built on top of React, allows you to build native mobile applications for iOS and Android. It shares code between web and mobile platforms, providing efficiency in development.
-
Angular: Ionic is a popular framework for building hybrid mobile applications using Angular. It uses web technologies (HTML, CSS, JavaScript) wrapped in a native container to create mobile apps.
Ultimately, the choice between React and Angular depends on your project requirements, team expertise, and personal preferences. React is more lightweight, flexible, and suitable for smaller projects or specific parts of larger applications. Angular is a comprehensive framework suitable for large-scale projects that require features like dependency injection, out-of-the-box tooling, and a structured approach.