React.js has quickly become one of the most popular JavaScript frameworks for building interactive user interfaces. Its declarative, component-based architecture allows creating complex UIs from small, isolated pieces of code that can be reused across your website or app.
In this comprehensive guide, you’ll learn fundamental React concepts by building a simple web app step-by-step from scratch. Along the way, you’ll understand how components, props, state, hooks, routing and other pieces fit together.
By the end, you’ll be equipped with the core knowledge needed to start building performant real-world apps using the power of React!
Chapter 1 - React Basics
Before we dive into code, let’s get familiar with React terminology and basic building blocks:
Components
React apps are made of isolated, reusable components that encapsulate markup, styles and behaviors into one package. For example, a Button, VideoPlayer, Like or Comment components.
These components receive data through attributes called props and manage their own private state. Combining components with distinct responsibilities allows crafting complex UIs.
JSX Syntax
React uses a JavaScript extension syntax called JSX to define component structures:
jsx
const element = <h1>Hello World</h1>;
It may look like HTML but gets transpiled to JavaScript under the hood. JSX allows mixing HTML-like tags with JavaScript capabilities.
This abstraction sacrificing separation of concerns for productivity and velocity. JSX along with components form core building blocks.
Virtual DOM
React keeps a fast in-memory representation of the DOM called virtual DOM. When components update state, a new virtual DOM gets created.
React then compares it with the previous one and only updates actual DOM elements that changed. This process reconciles differences efficiently minimizing expensive DOM mutations.
With key concepts covered, let’s start our coding journey!
Chapter 2 - Setup and First Component
We’ll use Create React App starter kit to bypass webpack configs and build setup hassles:
npx create-react-app my-first-react-app
cd my-first-react-app
npm start
This boots up a dev server with hot reloading allowing instant UI updates when editing files.
Let’s display a header text “Hello World” by creating `App.js` with our first component:
jsx
import React from 'react';
function App() {
return (
<h1>Hello World</h1>
);
}
export default App;
And render it inside `index.js`:
jsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
Voila, our shiny new component gets rendered!
READ ALSO:
Chapter 3 - JSX Syntax and Props
JSX may look like HTML but has some key differences like `className
` instead of `class` for adding CSS classes. Styles also must be defined as JavaScript objects:
#jsx
const divStyle = {
color: 'blue',
fontSize: '32px'
};
function App() {
return (
<div style={divStyle} className="container">
Hello World
</div>
);
}
We can pass data to components through custom attributes called `props`:
#jsx
function Welcome(props) {
return <h1>Hello {props.name}!</h1>;
}
function App() {
return (
<Welcome name="Sara" />
)
}
`name
` gets replaced by "Sara
" passed as a prop value.
Components receiving props behave like pure functions deriving output from inputs.
## Chapter 4 - State and Events
So far our UIs have been static. State allows components to dynamically update what they render:
#jsx
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
` hook initializes `count` state to 0. When button gets clicked, `setCount
` update function gets called changing `count` state. This triggers UI re-render displaying updated value.
Event listeners are attached to elements differently compared to plain JavaScript using `on[Event]
` convention like `onClick
`.
Chapter 5 - Effects and Refs
We’ve covered rendering UI declaratively through state. But what about imperative logic like data fetching?
Enter Effects!
`useEffect
` hook allows running arbitrary code conditionally based on component lifecycle:
//jsx
import { useState, useEffect } from 'react';
function GitHubUser(props) {
const [user, setUser] = useState(null);
useEffect(() => {
// Mimic fetching user data
setTimeout(() => {
setUser({
login: props.login,
name: 'Sample User'
});
}, 1000);
}, [props.login]);
if (!user) {
return <p>Loading...</p>;
}
return (
<div>{user.name}</div>
);
}
function App() {
return (
<GitHubUser login="sample-user" />
);
}
We imitated an async call with `setTimeout
` to fetch and update user after 1 second. This allows separating imperative logic from presentational UI declaratively.
Besides lifecycle methods, React also provides refs for direct DOM access when needed:
jsx
function Form() {
const inputRef = useRef();
function handleSubmit() {
inputRef.current.focus();
}
return (
<>
<input ref={inputRef} type="text"/>
<button onClick={handleSubmit}>Submit</button>
</>
);
}
So effects and refs act as escape hatches for DOM access outside declarative flow.
@post__cat_embed
Chapter 6 - Routing and Navigation
For multi-page apps, react-router handles routing:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
}
Wrapping components inside `<BrowserRouter>
` enables routing powered by `<Route>
` which matches path to components.
The react-router `<Link>
` component assists navigation between pages:
import { Link } from 'react-router-dom';
function Navbar() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
);
}
So routing connects isolated UI pieces into a complete user flow.
Chapter 7 - Fetching Data
We used `setTimeout
` earlier to mimic requests. The Fetch API allows sending asynchronous HTTP requests natively:
function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/users')
.then(res => res.json())
.then(data => setUsers(data))
}, []);
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
Promises simplify async logic without nesting callbacks. Fetch abstracts cross-browser differences behind a common interface.
For managing state across larger apps, use tools like React Query and SWR.
Chapter 8 - Context and Forms
Context API solves prop drilling across components:
const UserContext = React.createContext();
function UserProvider({children}) {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={user}>
{children}
</UserContext.Provider>
);
}
function Nav() {
const user = useContext(UserContext);
return <div>Welcome {user.name}!</div>;
}
Components can now consume context using the `useContext
` hook without explicitly passing through multiple layers.