React is a JavaScript library for building user interfaces. It is primarily used for building single-page applications and allows developers to create reusable UI components.
React’s diffing algorithm compares the Virtual DOM with the real DOM to update only what has changed, enhancing performance.
There are two main types of components:
// Example of a functional component
function Greeting() {
return <h1>Hello, World!</h1>;
}
export default Greeting;
// Example of a class component
import React, { Component } from 'react';
class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
export default Greeting;
State is used to manage data internal to a component and can change over time.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
useState
initializes the state (count
) and provides a function (setCount
) to update it.Props are used to pass data from parent to child components.
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}
function App() {
return <Welcome name="Alice" />;
}
Welcome
component receives the name
prop from its parent component App
.Vite is a modern build tool that provides a faster and leaner development experience for web projects.
Native ES Modules:
On-demand compilation:
Feature | Vite | Traditional Bundlers (e.g., Webpack) |
---|---|---|
Build Speed | Fast (ES Modules) | Slower (Requires bundling) |
HMR (Hot Reload) | Instant updates | Slower due to bundling |
Initial Setup | Minimal configuration | Complex configuration |
Dependency Handling | Optimized for modern browsers | Polyfills required for older browsers |
Once you create a project with Vite, it will have the following structure:
npm create vite@latest my-react-app --template react
cd my-react-app
npm install
npm run dev
my-react-app/
├── public/
├── src/
│ ├── App.jsx
│ ├── main.jsx
│ └── index.css
├── package.json
└── vite.config.js
// src/App.jsx
import { useState } from 'react';
function App() {
const [message, setMessage] = useState("Hello, World!");
return (
<div>
<h1>{message}</h1>
<button onClick={() => setMessage("Hello, Vite!")}>
Change Message
</button>
</div>
);
}
export default App;
npm (Node Package Manager) is the default package manager for JavaScript's Node.js runtime.
{
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"react": "^17.0.2"
},
"scripts": {
"start": "node server.js",
"build": "webpack --config webpack.config.js"
}
}
Yarn is another package manager for JavaScript, created by Facebook in 2016.
react@^17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz"
integrity sha512-Bt7Wl8pV...
Feature | npm | Yarn |
---|---|---|
Release Year | 2010 | 2016 |
Performance | Slower installs, especially in older versions | Faster installs (parallel downloads) |
Lockfile | Introduced in npm 5 (package-lock.json) | Built-in from the start (yarn.lock) |
Workspaces | Available, but more complex setup | Native support for monorepos |
npm install react
yarn add react
Both commands will install React, but Yarn does it more quickly due to its optimized performance.
useState
useEffect
useMemo
useContext
useState
is used to declare state variables in functional components.const [count, setCount] = useState(0);
Initializes a state variable count
with 0.
useEffect
handles side effects in components.useEffect(() => {
console.log("Component mounted");
}, []);
Executes once when the component mounts.
useMemo
memoizes expensive calculations to improve performance.const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
The function is only recalculated when a
or b
changes.
useContext
allows you to share state across components without passing props.const value = useContext(MyContext);
Retrieves the current value of MyContext
.
use
.We will create a custom hook to manage a counter's state.
import { useState } from 'react';
const useCounter = (initialValue = 0) => {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(initialValue);
return { count, increment, decrement, reset };
};
import React from 'react';
import useCounter from './useCounter';
const CounterComponent = () => {
const { count, increment, decrement, reset } = useCounter(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
};
This component uses the useCounter
hook to manage the counter's logic.
You can use useEffect
within custom hooks to handle side effects.
import { useState, useEffect } from 'react';
const useFetchData = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
};
import React from 'react';
import useFetchData from './useFetchData';
const DataComponent = () => {
const { data, loading, error } =
useFetchData('https://api.example.com/data');
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Fetched Data</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
};
This component fetches data using the useFetchData
custom hook.
useMemo
can be particularly useful when a component performs expensive calculations.import { useState, useMemo } from 'react';
const ExpensiveCalculation = () => {
const [count, setCount] = useState(0);
const [input, setInput] = useState('');
const expensiveCalculation = (num) => {
console.log('Calculating...');
return num * 2;
};
const memoizedValue =
useMemo(() => expensiveCalculation(count), [count]);
return (
<div>
<p>Memoized Value: {memoizedValue}</p>
<button onClick={() => setCount(count + 1)}>Increment Count</button>
<input value={input} onChange={(e) => setInput(e.target.value)} />
</div>
);
};
expensiveCalculation
will only run when count
changes, not on every re-render.
useMemo
:useMemo
:useContext
is ideal for global state management when you don't want to pass props down manually.createContext()
to define the global state.import React, { createContext, useState } from 'react';
export const ThemeContext = createContext();
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
export default ThemeProvider;
useContext
is used to consume values from the nearest provider.import React, { useContext } from 'react';
import { ThemeContext } from './ThemeProvider';
const ThemedComponent = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div style={{
background: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#000' : '#fff' }}>
<p>Current Theme: {theme}</p>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
};
ThemedComponent uses ThemeContext
to access the current theme and toggle it.
useReducer
is an alternative to useState
for managing complex state logic.useReducer
takes two arguments:const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
useReducer
.Dispatch sends an action to the reducer to change the state.
import { useReducer } from 'react';
const initialState = { count: 0 };
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
const CounterWithReducer = () => {
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>
);
};
useReducer
organizes state updates in a more declarative way using the reducer function.useState
allows more direct state management.useReducer
is often used alongside useContext
to manage complex state logic.import { useReducer, useContext } from 'react';
import { ThemeContext } from './ThemeProvider';
const themeReducer = (state, action) => {
switch (action.type) {
case 'LIGHT':
return 'light';
case 'DARK':
return 'dark';
default:
return state;
}
};
const ThemedComponentWithReducer = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
const [state, dispatch] = useReducer(themeReducer, theme);
return (
<div style={{
background: state === 'light' ? '#fff' : '#333',
color: state === 'light' ? '#000' : '#fff' }}>
<p>Current Theme with Reducer: {state}</p>
<button
onClick={() =>
dispatch({ type: state === 'light' ? 'DARK' : 'LIGHT' })}>
Toggle Theme with Reducer </button>
</div>
);
};
useReducer
and useContext
often work together to manage global state in React applications.useReducer
handles the logic for updating that state.import React, { createContext, useReducer, useContext } from 'react';
const AppContext = createContext();
const reducer = (state, action) => {
switch (action.type) {
case 'SET_USER':
return { ...state, user: action.payload };
default:
return state;
}
};
const initialState = { user: null };
export const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
};
export const useAppContext = () => useContext(AppContext);
The global state is stored in AppContext
and modified using useReducer
.
import React, { useEffect } from 'react';
import { useAppContext } from './AppProvider';
const UserProfile = () => {
const { state, dispatch } = useAppContext();
useEffect(() => {
const fetchUser = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
const userData = await response.json();
dispatch({ type: 'SET_USER', payload: userData });
};
fetchUser();
}, [dispatch]);
return (
<div>
<h1>User Profile</h1>
{state.user ? (
<pre>{JSON.stringify(state.user, null, 2)}</pre>
) : (
<p>Loading...</p>
)}
</div>
);
};
The UserProfile
component fetches user data and updates global state.
useState
and useReducer
to manage form inputs and handle form submissions.import React, { useState } from 'react';
const BasicForm = () => {
const [name, setName] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
alert(`Submitted: ${name}`);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
<button type="submit">Submit</button>
</form>
);
};
This form simply updates the name
state using useState
.
useReducer
is helpful when managing multiple form fields or complex form logic.This form uses useReducer
to manage both name
and email
states.
import React, { useReducer } from 'react';
const formReducer = (state, action) => {
switch (action.type) {
case 'SET_NAME':
return { ...state, name: action.payload };
case 'SET_EMAIL':
return { ...state, email: action.payload };
default:
return state;
}
};
const FormWithReducer = () => {
const [state, dispatch] = useReducer(formReducer, { name: '', email: '' });
const handleSubmit = (e) => {
e.preventDefault();
alert(`Submitted: Name - ${state.name}, Email - ${state.email}`);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
value={state.name}
onChange={(e) => dispatch({ type: 'SET_NAME', payload: e.target.value })}
/>
</label>
<br />
<label>
Email:
<input
type="email"
value={state.email}
onChange={(e) => dispatch({ type: 'SET_EMAIL', payload: e.target.value })}
/>
</label>
<button type="submit">Submit</button>
</form>
);
};
This form uses useReducer
to manage both name
and email
states.
useContext
, useReducer
, and form management.useReducer
to manage the list of tasks.useContext
to provide the task state globally across components.