Welcome to the world of React! This guide is designed to quickly bring you up to speed with the essential React concepts you’ll use every day in development. Whether you’re just starting out or looking to solidify your foundational knowledge, this article will cover approximately 80% of what you need to know to begin building impressive user interfaces with React.
What You Will Learn
- Creating and structuring React components
- Adding elements and applying styles using JSX
- Dynamically displaying data in your components
- Implementing conditional rendering and lists
- Handling user interactions and updating the UI
- Sharing data efficiently between components
Creating and Nesting Components
In React, everything is built from components. Think of a component as a self-contained piece of your user interface. It manages its own logic and appearance, ranging from small elements like buttons to entire pages or sections of your application.
React components are essentially JavaScript functions that return descriptions of what you want to see on the screen, known as markup. Here’s a simple example of a React component:
function MyButton() {
return (
<button>I'm a button</button>
);
}
This code snippet defines a functional component called MyButton
. The function returns JSX, which looks like HTML but is actually a syntax extension to JavaScript that allows you to describe UI structures.
To use this component, you can nest it within another component. For example, let’s create an MyApp
component that includes our MyButton
:
export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
It’s crucial to note that React components must start with a capital letter (like MyButton
and MyApp
). This is how React distinguishes them from regular HTML tags, which are always lowercase.
Let’s see how this renders:
function MyButton() {
return (
<button>I'm a button</button>
);
}
export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
The export default
keyword indicates the primary component of this file, making it accessible to other parts of your application. If you’re new to JavaScript syntax like export default
, resources like MDN and javascript.info offer comprehensive explanations.
Writing Markup with JSX
The syntax used to create UI elements in React, as seen in the previous examples, is called JSX (JavaScript XML). While JSX is optional, it’s widely adopted in the React community due to its expressiveness and ease of use in describing user interfaces directly within your JavaScript code. Most modern React development tools, as recommended in guides for setting up a local React development environment, provide built-in support for JSX.
JSX has stricter rules than HTML. For instance, all tags must be explicitly closed (e.g., <br />
). Additionally, a React component cannot return multiple JSX elements at the top level. They must be wrapped in a single parent element, such as a <div>...</div>
or a fragment <>...</>
(an empty wrapper).
function AboutPage() {
return (
<>
<h1>About</h1>
<p>Hello there.<br />How do you do?</p>
</>
);
}
If you’re transitioning from HTML and need to convert existing HTML code to JSX, various online converters can automate this process, helping you quickly adapt your markup for React.
Adding Styles
Styling in React often involves using CSS classes, similar to traditional web development. In JSX, you specify a CSS class using className
, mirroring the HTML class
attribute.
<img className="avatar" />
You would then define the styles for this class in a separate CSS file:
/* In your CSS file */
.avatar {
border-radius: 50%;
}
React gives you flexibility in how you manage CSS files. In basic setups, you might include CSS files directly in your HTML. However, if you’re using build tools or frameworks, they often provide specific conventions for including and managing CSS within your React projects. Consult the documentation for your specific setup to learn the best practices for integrating CSS.
Displaying Data
JSX’s power comes from its ability to blend markup with JavaScript. Curly braces {}
in JSX act as an “escape hatch” back into JavaScript, allowing you to embed variables and expressions directly into your markup. This is fundamental for displaying dynamic data. For example, to display a user’s name stored in a JavaScript variable user.name
, you can do this:
return (
<h1>
{user.name}
</h1>
);
You can also use these curly braces within JSX attributes. However, instead of using quotes for attribute values, you use curly braces. For example, className="avatar"
sets the CSS class to the string “avatar”, while src={user.imageUrl}
retrieves the value of the JavaScript variable user.imageUrl
and sets it as the src
attribute for the image.
return (
<img
className="avatar"
src={user.imageUrl}
/>
);
JSX is quite flexible; you can embed more complex JavaScript expressions within these curly braces, such as string concatenation or function calls.
Try this example in CodeSandbox
const user = {
name: 'Hedy Lamarr',
imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
imageSize: 90,
};
export default function Profile() {
return (
<>
<h1>{user.name}</h1>
<img
className="avatar"
src={user.imageUrl}
alt={'Photo of ' + user.name}
style={{
width: user.imageSize,
height: user.imageSize
}}
/>
</>
);
}
In this example, style={{}}
might look unusual, but it’s simply a standard {}
JavaScript object nested inside the style={}
JSX curly braces. This pattern is used when you need to apply styles that depend on JavaScript variables, offering a dynamic approach to inline styling.
Conditional Rendering
React doesn’t introduce a new syntax for conditional logic within JSX. Instead, you leverage standard JavaScript conditional statements. For instance, you can use an if
statement to conditionally render different JSX based on a condition:
let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
</div>
);
For more concise conditional rendering, especially within JSX, you can use the conditional ?
operator. Unlike if
statements, the ternary operator works directly inside JSX:
<div>
{isLoggedIn ? (
<AdminPanel />
) : (
<LoginForm />
)}
</div>
When you only need to render something conditionally if a condition is true (and nothing otherwise), you can use the logical &&
operator for a more compact syntax:
<div>
{isLoggedIn && <AdminPanel />}
</div>
All these JavaScript conditional patterns are valid for conditionally rendering JSX elements or even attributes. If you’re new to these JavaScript concepts, starting with if...else
for clarity is perfectly acceptable.
Rendering Lists
To display lists of data in React, you’ll typically use JavaScript features like the for
loop or, more commonly in React, the array map()
function.
Consider an array of product objects:
const products = [
{ title: 'Cabbage', id: 1 },
{ title: 'Garlic', id: 2 },
{ title: 'Apple', id: 3 },
];
To render a list of these products, you can use the map()
function to transform the array of products
into an array of JSX <li>
items:
const listItems = products.map(product =>
<li key={product.id}>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
The key
attribute in the <li>
element is essential. For each item in a list, you should provide a unique key
, which should be a string or number that distinctly identifies that item among its siblings. Often, this key
comes directly from your data, such as a database ID. React uses these keys to efficiently update and re-render lists, especially when items are added, removed, or reordered.
Experiment with this code on CodeSandbox
const products = [
{ title: 'Cabbage', isFruit: false, id: 1 },
{ title: 'Garlic', isFruit: false, id: 2 },
{ title: 'Apple', isFruit: true, id: 3 },
];
export default function ShoppingList() {
const listItems = products.map(product =>
<li
key={product.id}
style={{
color: product.isFruit ? 'magenta' : 'darkgreen'
}}
>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
}
Responding to Events
React allows you to make your UI interactive by responding to user events, like clicks, form submissions, and more. You can handle these events by declaring event handler functions within your components.
function MyButton() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}
Notice in onClick={handleClick}
that we’re passing the handleClick
function itself, not calling it. There are no parentheses after handleClick
. You pass the function as a reference, and React will call your event handler function when the button is clicked.
Updating the Screen
In many applications, you need components to “remember” information and update the display based on interactions or data changes. For this, React uses state. State allows components to re-render and reflect changes over time.
To add state to a component, you start by importing the useState
Hook from React:
import { useState } from 'react';
Then, inside your component, you can declare a state variable using useState
:
function MyButton() {
const [count, setCount] = useState(0);
// ...
}
useState
returns an array with two elements: the current state value (count
in this case) and a function to update it (setCount
). You can name these as you like, but the convention is to use [stateVariable, setStateFunction]
.
The initial value of the state is set by passing it as an argument to useState()
. In this case, count
starts at 0
. To update the state, you call setCount()
with the new value. For example, to increment the counter when a button is clicked:
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
When setCount(count + 1)
is called, React re-renders the MyButton
component. During this re-render, count
will now have the updated value (1
, then 2
, and so on).
If you render the same component multiple times, each instance maintains its own independent state. Clicking a button in one instance will only update the state for that specific button, not others.
Run this example on CodeSandbox
import { useState } from 'react';
export default function MyApp() {
return (
<div>
<h1>Counters that update separately</h1>
<MyButton />
<MyButton />
</div>
);
}
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
Each MyButton
independently tracks its count
state, demonstrating the encapsulated nature of component state in React.
Using Hooks
Functions that begin with use
, like useState
, are known as Hooks. useState
is a built-in Hook provided by React, and you can find other essential built-in Hooks in the React API reference. You also have the power to create your own custom Hooks by combining existing ones to encapsulate reusable stateful logic.
Hooks have a strict rule: they must be called at the top level of your components (or within other Hooks). You cannot call Hooks inside conditions, loops, or nested functions within a component. If you need to use stateful logic in such scenarios, extract that logic into a new component or a custom Hook.
Sharing Data Between Components
In the previous counter example, each MyButton
component managed its own count
independently. However, often you’ll need components to share data and update together.
To achieve this, you need to “lift state up.” Instead of each MyButton
managing its own count
, you move the state to the closest common ancestor component that contains all the components needing that state.
In our example with two MyButton
components, the common ancestor is MyApp
. We’ll move the count
state to MyApp
.
Initially, MyApp
holds the count
state (set to 0
) and passes it down to both MyButton
children.
When a button is clicked, MyApp
updates its count
state, and this updated value is then passed down to both MyButton
components, causing them to re-render with the new count.
Here’s how to modify the code to lift the state up:
First, move the useState
and handleClick
logic from MyButton
to MyApp
:
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update together</h1>
<MyButton />
<MyButton />
</div>
);
}
function MyButton() {
// ... logic is moved to MyApp ...
}
Next, pass the count
and handleClick
down as props to each MyButton
component. Props are how components pass data to their children in React. You use JSX curly braces to pass values as props, similar to how you set attributes for built-in HTML tags.
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update together</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}
Now, MyApp
holds the count
state and the handleClick
function and passes them down to MyButton
as props named count
and onClick
.
Finally, update MyButton
to receive and use these props:
function MyButton({ count, onClick }) {
return (
<button onClick={onClick}>
Clicked {count} times
</button>
);
}
When a button is clicked, the onClick
prop (which is the handleClick
function from MyApp
) is executed. This updates the count
state in MyApp
, and because count
is passed as a prop to MyButton
, both buttons re-render to display the updated count
. This pattern of “lifting state up” is fundamental for sharing state and behavior across components in React.
Try this shared state example on CodeSandbox
import { useState } from 'react';
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update together</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}
function MyButton({ count, onClick }) {
return (
<button onClick={onClick}>
Clicked {count} times
</button>
);
}
Next Steps in React Learning
Congratulations! You’ve now grasped the fundamental concepts of React. You’re well-equipped to dive deeper and start building more complex applications.
To solidify your understanding and put these concepts into practice, check out the official React Tutorial. It guides you through building a classic Tic-Tac-Toe game, reinforcing what you’ve learned and introducing more React features along the way. Happy coding!