
What is React.js?
First, let’s talk about the basics.
React is a popular JavaScript library for building user interfaces (UI). It is used for creating reusable UI components that can be rendered on the frontend of a web application. React is known for its efficient rendering of large data sets, making it an ideal choice for building complex and high-performance applications.
React was created by Facebook and is used by many large companies to build their web applications. It can be used in combination with other libraries and frameworks, such as Redux and React Router, to create complete web applications.
React uses a declarative approach to programming, which means that developers specify what they want the UI to look like, and React takes care of rendering the UI to the screen. This makes it easier to develop and maintain complex applications because the code is easier to understand and debug.
What is a React hook?
A React hook is a special function that allows you to “hook into” the React framework and add additional functionality to a React component. React hooks were introduced in React 16.8 and are a way to use state and other React features without writing a class.
In React, a hook is a function that enables you to add additional functionality to a component by “hooking into” one of React’s lifecycle methods or other built-in features. There are several built-in hooks in React, such as useState
for adding state to a functional component, and useEffect
for running side effects in a functional component.
There are several React hooks, including useContext
, useReducer
, useCallback
, useMemo
, useRef
, useImperativeHandle
, useLayoutEffect
, and useDebugValue
. However, you can also create your own custom hooks in React to extract and share behavior that is common to many components. This can help to reduce duplication of code and make your application easier to maintain. And that’s the magic of React.
React Hooks 101:
useState
useState
: This hook allows you to add state to functional components. It returns a pair of values: the current state and a function that updates it.
import React, { useState } from 'react';
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>
);
}
In this example, we use the useState
hook to add a count
state variable to the Example
component. The initial value of count
is set to 0. We then use the setCount
function to update the count
state variable when the button is clicked. This will cause the component to re-render and display the updated value of count
.
useEffect
useEffect
: This hook is used to perform side effects in functional components. It is called after every render, including the first render.
import React, { useState, useEffect } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
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`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
In this example, we use the useEffect
hook to perform an effect after every render of the Example
component. In this case, the effect updates the document title with the current value of the count
state variable.
The useEffect
hook is called after every render, including the first render. This is similar to the componentDidMount
and componentDidUpdate
lifecycle methods in class-based components.
useContext
useContext
: This hook lets you access the React context in a functional component. It allows you to avoid passing props down manually through multiple levels of components.
import React, { useContext } from 'react';
// First, create a context
const MyContext = React.createContext();
// Then, create a provider
function MyProvider({ children }) {
const [state, setState] = useState({
name: 'Ilolo',
age: 25,
});
return (
<MyContext.Provider value={{ state, setState }}>
{children}
</MyContext.Provider>
);
}
// Finally, create a consumer
function MyComponent() {
const { state, setState } = useContext(MyContext);
return (
<div>
<p>Name: {state.name}</p>
<p>Age: {state.age}</p>
<button onClick={() => setState({ ...state, age: state.age + 1 })}>
Increase age
</button>
</div>
);
}
In this example, we use the useContext
hook to consume the MyContext
context in the MyComponent
component. We use the state
and setState
values from the context to display and update the name
and age
state variables.
The useContext
hook is a convenient way to access the React context in a functional component, without the need to pass props down manually through multiple levels of components.
useReducer
useReducer
: This hook is similar to useState
, but it lets you manage complex state logic in a reducer function. It is typically used for managing state that depends on the previous 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();
}
}
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>
);
}
In this example, we use the useReducer
hook to manage state in the Counter
component. The hook takes a reducer function and an initial state as arguments, and returns the current state and a dispatch function.
We use the dispatch function to dispatch actions that update the state according to the reducer function. In this case, we have actions for incrementing and decrementing the count
state variable.
The useReducer
hook is a powerful way to manage complex state logic, especially when the state depends on the previous state. It is similar to the redux
library, but it is built into React and can be used without additional dependencies.
useCallback
useCallback
: This hook returns a memoized callback. It is useful for optimizing performance by avoiding unnecessary re-renders.
import React, { useState, useCallback } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// Only re-create the callback if count changes
const increment = useCallback(() => setCount(c => c + 1), [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>
Increment
</button>
</div>
);
}
In this example, we use the useCallback
hook to create a memoized callback for the increment
function. The useCallback
hook takes a function and a list of dependencies as arguments, and returns a memoized version of the function.
The memoized function will only be re-created if one of its dependencies changes. In this case, the increment
function only depends on the count
state variable, so it will only be re-created if count
changes.
This is useful for optimizing performance by avoiding unnecessary re-renders. For example, if the increment
function were not memoized, the component would re-render every time the increment
function is called, even if the count
state variable has not changed.
useMemo
useMemo
: This hook returns a memoized value. It is useful for optimizing performance by avoiding expensive calculations on every render.
import React, { useState, useMemo } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// Only re-compute the value if count changes
const double = useMemo(() => count * 2, [count]);
return (
<div>
<p>Count: {count}</p>
<p>Double: {double}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
In this example, we use the useMemo
hook to memoize the double
value. The useMemo
hook takes a function and a list of dependencies as arguments, and returns a memoized value.
The memoized value will only be re-computed if one of its dependencies changes. In this case, the double
value depends on the count
state variable, so it will only be re-computed if count
changes.
This is useful for optimizing performance by avoiding expensive calculations on every render. For example, if the double
value were not memoized, it would be re-computed on every render, even if the count
state variable has not changed.
useRef
useRef
: This hook creates a mutable ref object. It is useful for keeping a reference to a value that does not change between renders.
import React, { useRef } from 'react';
function TextInput() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>
Focus the input
</button>
</div>
);
}
In this example, we use the useRef
hook to create a mutable ref object and attach it to the input
element. The useRef
hook takes an initial value as an argument, and returns a mutable ref object.
We use the current
property of the ref object to access the current value of the ref. In this case, the current value is the input
element.
This is useful for keeping a reference to a value that does not change between renders. For example, if we did not use a ref, we would not be able to call the focus
method on the input
element from the handleClick
function.
useImperativeHandle
useImperativeHandle
: This hook customizes the instance value that is exposed to parent components when using the ref
prop.
import React, { useRef, useImperativeHandle } from 'react';
function TextInput({ forwardRef }) {
const inputRef = useRef(null);
useImperativeHandle(forwardRef, () => ({
focus: () => {
inputRef.current.focus();
},
}));
return <input ref={inputRef} type="text" />;
}
// Note: you must specify the forwardRef prop in the parent component
const Parent = React.forwardRef((props, ref) => (
<TextInput {...props} forwardRef={ref} />
));
In this example, we use the useImperativeHandle
hook to customize the instance value that is exposed to the parent component. The useImperativeHandle
hook takes a ref object and a function that returns an object of exposed values as arguments.
The exposed values are available to the parent component through the ref object. In this case, we expose the focus
method of the input
element, so that the parent component can call it.
This is useful for implementing advanced patterns like exposing imperative APIs to parent components. For example, if we did not use useImperativeHandle
, the parent component would not be able to call the focus
method on the input
element.
useLayoutEffect
useLayoutEffect
: This hook is similar to useEffect
, but it runs synchronously after all DOM mutations. It is useful for performing calculations that need to be synchronized with the browser.
import React, { useState, useLayoutEffect } from 'react';
function Counter() {
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
// Measure the width of the element
const element = document.getElementById('counter');
setWidth(element.offsetWidth);
});
return (
<div id="counter">
<p>Width: {width}px</p>
</div>
);
}
In this example, we use the useLayoutEffect
hook to measure the width of the div
element in the Counter
component. The useLayoutEffect
hook takes a function as an argument, and runs the function synchronously after all DOM mutations.
This is useful for performing calculations that need to be synchronized with the browser. For example, if we used the useEffect
hook instead, the width would not be measured until after the next browser repaint, which may be too late for some applications.
useDebugValue
useDebugValue
: This hook can be used to display a label for a custom hook in the React developer tools. It is useful for debugging and identifying hooks in the application.
import { useDebugValue } from 'react';
function useCounter(initialCount) {
const [count, setCount] = useState(initialCount);
// Display a label for the hook in the React developer tools
useDebugValue(`Count: ${count}`);
return [count, setCount];
}
In this example, we use the useDebugValue
hook to display a label for the useCounter
hook in the React developer tools. The useDebugValue
hook takes a value as an argument, and displays it next to the hook in the React developer tools.
This is useful for debugging and identifying hooks in the application. For example, if you have multiple instances of the useCounter
hook in your application, the labels will help you to distinguish them from each other in the React developer tools.
Custom Hooks
In React JS, a custom hook is a function that performs a specific action and can be reused throughout a React application. Custom hooks allow you to extract and share behavior that is common to many components. This can help to reduce duplication of code and make your application easier to maintain.
Custom hooks are created by defining a JavaScript function that starts with the word use
and contains the logic for the specific behavior you want to reuse. You can then call this hook from any other React component in your application.
Here is an example of a custom hook in React JS that keeps track of the current time:
import React, { useState, useEffect } from 'react';
function useCurrentTime() {
const [time, setTime] = useState(new Date());
useEffect(() => {
const interval = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(interval);
}, []);
return time;
}
function Clock() {
const time = useCurrentTime();
return <div>The current time is {time.toLocaleString()}.</div>;
}
In this example, the useCurrentTime
hook uses the useState
and useEffect
hooks to create a stateful value that updates every second with the current time. The Clock
component uses this hook to display the current time.