React Tutorial_ Beginner to Advanced
React Tutorial_ Beginner to Advanced
Introduction to React
React is a popular JavaScript library for building user interfaces, particularly for
single-page applications. It was developed by Facebook and is known for its
component-based approach, virtual DOM, and declarative style.
What is React?
● Component-Based: Build UIs by breaking them into reusable components.
● Virtual DOM: Efficiently updates the actual DOM, improving performance.
● Declarative: You describe what the UI should look like, and React handles the
rendering.
Why Learn React?
● Popularity: Large community and extensive ecosystem.
● Performance: Optimized for speed.
● Reusability: Components can be reused throughout the application.
● Flexibility: Can be used for web and mobile development (React Native).
● Job Market: High demand for React developers.
node -v
npm -v
This will:
● Create a new folder called my-react-app.
● Set up the basic React project structure.
● Start a development server.
Open your browser and navigate to http://localhost:3000 to see your React app.
Core Concepts
1. Components
Components are the building blocks of a React application. They are reusable pieces
of UI that can be composed to create complex interfaces.
Function Components
import React from 'react';
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
Class Components
import React, { Component } from 'react';
● Looks like HTML, but it's transformed into JavaScript function calls.
● Allows you to embed JavaScript expressions using curly braces {}.
3. Props
Props (properties) are used to pass data from a parent component to a child
component.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="John" />
<Welcome name="Jane" />
</div>
);
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<h1>Count: {this.state.count}</h1>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
function Counter() {
const [count, setCount] = useState(0);
5. Hooks
Hooks are functions that let you "hook into" React state and lifecycle features from
function components. They were introduced in React 16.8.
Common Hooks
● useState: For adding state to function components (as seen above).
● useEffect: For performing side effects (data fetching, subscriptions, DOM
manipulation).
● useContext: For consuming values from a context.
● useReducer: For more complex state management.
useEffect
The useEffect Hook lets you perform side effects in function components.
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
● The callback function inside useEffect runs after the component renders.
● The optional second argument (the array [count]) is an array of dependencies. If
the values in the array don't change between renders, the effect won't re-run. An
empty array [] means the effect runs only once on mount and unmount. If you
omit the second argument, the effect runs after every render.
6. Conditional Rendering
Conditional rendering allows you to render different elements or components based
on certain conditions.
Using if statements
function Greeting(props) {
if (props.isLoggedIn) {
return <h1>Welcome back!</h1>;
}
return <h1>Please sign up.</h1>;
}
function App() {
return (
<div>
<Greeting isLoggedIn={true} />
<Greeting isLoggedIn={false} />
</div>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
● The key should be a stable identifier for each item, such as an ID from a
database.
● Avoid using the array index as the key if the order of items may change.
8. Forms
React handles forms differently than traditional HTML. Form data is typically managed
using state.
Controlled Components
In a controlled component, the form elements' values are controlled by React state.
function MyForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
<br />
<label>
Email:
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</label>
<br />
<button type="submit">Submit</button>
</form>
);
}
● The value attribute of the input elements is bound to the component's state.
● The onChange handler updates the state when the input value changes.
9. Lifting State Up
When several components need to share the same data, you lift the state up to their
closest common ancestor.
function Parent() {
const [value, setValue] = useState('');
return (
<div>
<Child1 value={value} onChange={handleChange} />
<Child2 value={value} />
</div>
);
}
Advanced Concepts
1. Context
Context provides a way to pass data through the component tree without having to
pass props down manually at every level.
Creating a Context
import React, { createContext, useContext, useState } from 'react';
function App() {
const [value, setValue] = useState('Hello, Context!');
return (
<MyContext.Provider value={value}>
<ComponentA />
</MyContext.Provider>
);
}
Consuming a Context
function ComponentC() {
const value = useContext(MyContext);
return <div>Component C: {value}</div>;
}
function ComponentB() {
return <ComponentC/>
}
function ComponentA() {
return <ComponentB/>
}
2. Render Props
A render prop is a function prop that a component uses to share code with other
components.
render() {
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
);
}
}
function MouseTracker() {
return (
<Mouse
render={({ x, y }) => (
<h1>The mouse position is ({x}, {y})</h1>
)}
/>
);
}
function withLogging(WrappedComponent) {
return function EnhancedComponent(props) {
const [logMessage, setLogMessage] = useState('');
console.log(`Component ${WrappedComponent.name} is rendering`);
return (
<>
<WrappedComponent {...props} setLogMessage={setLogMessage} />
{logMessage && <div>Log: {logMessage}</div>}
</>
);
};
}
function MyComponent(props) {
const {setLogMessage} = props;
return (
<div>
<h1>My Component</h1>
<button onClick={() => setLogMessage("Clicked!")}>Click</button>
</div>
);
}
function App() {
return (
<LoggedComponent />
)
}
function MyInput() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus(); // Focus the input element on mount
}, []);
return (
<input type="text" ref={inputRef} />
);
}
5. Error Boundaries
Error boundaries are class components that catch JavaScript errors anywhere in their
child component tree, log those errors, and display a fallback UI instead of the
crashed component tree.
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.error(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return (
<div>
<h2>Something went wrong.</h2>
<p>Please try again later.</p>
</div>
);
}
return this.props.children;
}
}
function MyComponent() {
throw new Error("Error from MyComponent");
}
function App() {
return (
<MyErrorBoundary>
<MyComponent />
</MyErrorBoundary>
);
}
6. Fragments
Fragments let you group a list of children without adding an extra node to the DOM.
function MyComponent() {
return (
<>
<h1>Hello</h1>
<p>World</p>
</>
);
}
return ReactDOM.createPortal(
<div style={{
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
backgroundColor: 'rgba(0,0,0,0.5)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
zIndex: 1000
}}>
<div style={{
backgroundColor: 'white',
padding: '20px',
borderRadius: '5px',
width: '50%',
maxWidth: '500px'
}}>
{children}
<button onClick={onClose}>Close</button>
</div>
</div>,
modalRoot
);
}
function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div>
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>
<MyModal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
<h1>Modal Content</h1>
<p>This content is rendered outside the App component's DOM tree.</p>
</MyModal>
</div>
);
}
React.memo
React.memo is a higher-order component that memoizes function components.
function App() {
const [count, setCount] = useState(0);
const [otherCount, setOtherCount] = useState(0);
return (
<div>
<MyComponent value={count} />
<button onClick={() => setCount(count + 1)}>Increment Count</button>
<button onClick={() => setOtherCount(otherCount + 1)}>Increment Other
Count</button>
</div>
);
}
function App() {
const [a, setA] = useState(1);
const [b, setB] = useState(2);
return (
<div>
<h1>Result: {result}</h1>
<button onClick={() => setA(a + 1)}>Increment A</button>
<button onClick={() => setB(b + 1)}>Increment B</button>
</div>
);
}
● useMemo only recomputes the value when its dependencies ([a, b]) change.
● Useful for expensive calculations or creating stable references.
useCallback
The useCallback Hook returns a memoized callback function.
function App() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(c => c + 1);
}, []); // The dependency array is empty, so the function never changes.
return (
<div>
<MyComponent onClick={increment} />
<h1>Count: {count}</h1>
</div>
);
}
● useCallback returns the same function instance across renders, unless its
dependencies change.
● Useful for passing callbacks to optimized child components.
9. Web Workers
Web Workers provide a way to run JavaScript code in a background thread, without
blocking the main thread. This can improve the performance of your React
applications by offloading expensive computations or tasks to a separate thread.
function App() {
const [result, setResult] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
const myWorker = new Worker(new URL('./worker.js', import.meta.url));
return () => {
// Cleanup logic when the component unmounts or before re-running effect
myWorker.terminate(); // Terminate the worker to prevent memory leaks
};
}, []);
return (
<div>
<button onClick={runWorker} disabled={loading}>
{loading ? 'Loading...' : 'Run Worker'}
</button>
{result !== null && <h1>Result: {result}</h1>}
</div>
);
}
Types of Tests
● Unit Tests: Test individual components in isolation.
● Integration Tests: Test how components interact with each other.
● End-to-End (E2E) Tests: Test the application as a whole, simulating user
behavior.
Tools
● Jest: A popular JavaScript testing framework (often used with React).
● React Testing Library: A library for testing React components that focuses on
testing from the user's perspective.
● Cypress: A tool for E2E testing.
● Selenium/WebDriver: Another tool for E2E testing.
2. Create a Component:
// src/components/Counter.js
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>Increment</button>
</div>
);
}
export default Counter;
fireEvent.click(button);
expect(countDisplay).toHaveTextContent('Count: 1');
});
React Router
React Router is the most popular routing library for React.
1. Install React Router:
npm install react-router-dom
2. Set up Routing:
import React from 'react';
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
function Home() {
return <h2>Home Page</h2>;
}
function About() {
return <h2>About Page</h2>;
}
function App() {
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</div>
</Router>
);
}
● BrowserRouter enables client-side routing.
● Routes and Route define the mapping between URLs and components.
● Link is used for navigation. It prevents a full page reload.
● useParams hook can be used to access route parameters.
12. Redux
Redux is a state management library for JavaScript applications. It provides a
centralized store for managing the state of your application, making it easier to
reason about and debug.
Core Concepts
● Store: Holds the application's state.
● Actions: Plain JavaScript objects that describe an event that occurred.
● Reducers: Functions that specify how the application's state changes in
response to actions.
● Dispatch: A function used to send actions to the store.
● Selectors: Functions that extract specific pieces of data from the store.
Example
1. Install Redux and React Redux:
npm install redux react-redux
2. Define Actions:
// src/actions/counterActions.js
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
3. Define Reducers:
// src/reducers/counterReducer.js
import { INCREMENT, DECREMENT } from '../actions/counterActions';
function Counter(props) {
return (
<div>
<h1>Count: {props.count}</h1>
<button onClick={props.increment}>Increment</button>
<button onClick={props.decrement}>Decrement</button>
</div>
);
}
const mapDispatchToProps = {
increment,
decrement
};
function App() {
return (
<Provider store={store}>
<ConnectedCounter />
</Provider>
);
}
3. Define a Query:
import { gql } from '@apollo/client';
function Characters() {
const { loading, error, data } = useQuery(GET_CHARACTERS, {client: client});
return (
<div>
<h1>Characters</h1>
<ul>
{data.characters.results.map((character) => (
<li key={character.id}>{character.name}</li>
))}
</ul>
</div>
);
}
function App() {
return (
<ApolloProvider client={client}>
<Characters />
</ApolloProvider>
)
}