Get Started in JavaScript Episode 5: Frontend Frameworks (Reactjs) - Part 1
Join us for Episode 5 as we dive into the world of React. Learn how to set up projects, understand component-based architecture, and master state management.
In the previous episodes, we covered the fundamentals of JavaScript including variables, functions, scopes, arrays, objects and much more. We also learned how to manipulate the DOM and handle events. In this episode, we will explore frontend frameworks and start learning React - one of the most popular frameworks used for building user interfaces.
Table of Contents
- What are frontend frameworks
- Why React
- Setting up a React project
- Components and component-based architecture
- State management
What are frontend frameworks?
Frontend frameworks help with website building. They do lots of work like updating pages when changes are made and let you let focus on design instead of low-level code. This helps build sites faster..
Introduction to React
React is a popular frontend framework developed by Facebook. Some key advantages are its use of a declarative programming model called JSX, improved performance via the virtual DOM, and its modular component-based architecture.
React uses JSX, which extends JavaScript with XML-like syntax. This mixes template and logic in one place and avoids separating technologies. Babel compiles JSX down to regular JavaScript calls.
The virtual DOM in React also provides a major performance boost over traditional DOM manipulation. Instead of directly updating the browser DOM on each change, React tracks a virtual representation internally. It then compares the virtual to real DOM and only updates what changed.
Large companies like Netflix, Facebook, and Airbnb all use React extensively because it handles complexity well at scale through its reusable component model. Components help manage app state and structure the UI from small nested reusable pieces.
Prerequisite
Node.js is required to run the Create React App command. You can download and install Node.js from the official website by following there guide here.
Then verify installation:node -v
and npm -v
node -v
npm -v
Setting up a React project
To create a React project, we first need to install a few packages. Open up your terminal and run:
npx create-react-app my-app
This will generate a starter React project with all the necessary build tools preconfigured. Change into the project directory and run npm start
to spin up a development server. You should see a simple React app rendered in the browser!
Components and component-based architecture
Components allow us to split the UI into independent, reusable pieces. A component renders some UI from props (properties) and manages its own state (data).
For example, we can create an App component that renders a Header and Main content:
function App() {
return (
<div>
<Header />
<Main />
</div>
);
}
We break complex UIs into simple reusable components like Buttons, Cards and Forms. This makes code more modular and maintainable. Components separate concerns and encapsulate behavior.
State management
State in React refers to any data that changes over time in response to user actions, network responses etc. We can track state locally using the useState hook:
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
We initialize count to 0 and increment it when the button is clicked using setCount. useState takes an initial value as the first argument and returns an array with the current state value and setter function.
useState()
useState is one of the basic React hooks that allows function components to have local state. Previously, state could only be added to class components using this.state.
When you call useState inside a component, it returns an array with two elements - the current state value and a function to update it.
For example:
const [count, setCount] = useState(0);
- count is the current state value
- setCount is the function to call to update the state
- 0 is the initial state value passed to useState
When you call setCount, it triggers a re-render of the component. React then uses the new value return by setCount as the current state value.
For example:
setCount(prevCount => prevCount + 1);
This calls setCount and passes a function that returns the previous count incremented by 1.
useState also accepts the previous state value as the first argument to the updater function, to ensure we don't lose any pending state updates.
Here is an example of using useState to manage multiple state variables in a component:
function MyComponent() {
const [name, setName] = useState('');
const [age, setAge] = useState(0);
const handleNameChange = (e) => {
setName(e.target.value);
}
const handleAgeChange = (e) => {
setAge(e.target.value);
}
return (
<div>
<input
value={name}
onChange={handleNameChange}
/>
<input
value={age}
onChange={handleAgeChange}
/>
<p>Name: {name}</p>
<p>Age: {age}</p>
</div>
)
}
Here we are:
- Declaring two state variables - name and age using useState
- Initializing name to an empty string and age to 0
- Creating change handler functions for each input
- Updating the relevant state on input change using setName/setAge
- Rendering the current state values
When any input changes, only that state variable will update. React will re-render the component and display the new values.
We can have as many state variables as we need by calling useState multiple times. Each will be independent and updating one won't affect others.
This allows building dynamic UIs with fine-grained control over multiple pieces of local component state using the React hook model.
Some key things to note:
- useState declarations must be at the top level of the component
- You can have multiple state variables by calling useState multiple times
- State updates may be asynchronous, use the callback updater function for consistency
- React batches re renders to improve performance
So in summary, useState allows us to add reactive data to function components that will trigger re-renders when updated. This makes state management very simple using hooks!
Build a todo list app
To test our knowledge, let's build a todo list app that implements all the concepts we learned in this episode: components, props, state etc. We'll build it step-by-step below so you can follow along!
First, open up your terminal. We need to create a new React project. Type:
npx create-react-app todo-list-app
create-react-app
is a handy tool that sets up everything we need to get started. It generates a folder called todo-list-app with all our React files inside.
Now, change into that folder:
cd todo-list-app
We're all set up! Next, let's fire up the development server:
npm start
Boom! You should see your new React app running in the browser. We'll be building on this same page.
Ok friend, open up App.js
and replace it with code below. This is where all the magic will happen:
import React from "react";
function App() {
return (
<div>
<h1>To-Do List!</h1>
</div>
);
}
export default App;
Pretty simple right? The component renders some JSX, which is like HTML but for React. We give it a header to name our list.
Let's add an input field and button below it:
//...
return (
<div>
<h1>To-Do List</h1>
<input type="text" placeholder="Enter a task"/>
<button>Add</button>
</div>
);
//...
Now we have a place for the user to enter tasks and a button to submit them. But it doesn't do anything yet - we'll fix that soon!
We're using state to manage the dynamic data in our app. State allows components to re-render when data changes.
To access state, we use the useState hook. It returns an array with the current state value and a function to update it:
const [todos, setTodos] = useState([]);
Here todos will start as an empty array to hold our list items.
We also track the input value with state:
const [newTodo, setNewTodo] = useState('');
Then we define a handler function:
const handleInputChange = (e) => {
setNewTodo(e.target.value);
}
This updates newTodo state every time the user types.
Now when the user clicks "Add", we call:
const addTodo = () => {
setTodos([...todos, newTodo]);
setNewTodo(); // Clear the todo we just added.
}
The spread operator ...todos copies the existing array, then adds newTodo to the end. This immutable way of updating objects is preferred in React.
We can then render the todos array as a list:
// ..
<ul>
{todos.map(todo => (
<li>{todo}</li>
))}
</ul>
// ..
// App.js
import React from "react";
function App() {
const [newTodo, setNewTodo] = React.useState('');
const [todos, setTodos] = React.useState([]);
const addTodo = () => {
setTodos([...todos, newTodo]);
setNewTodo();
}
const handleInputChange = (e) => {
setNewTodo(e.target.value);
}
return (
<div>
<h1>To-Do List</h1>
<input type="text" placeholder="Enter a task" value={newTodo} onChange={handleInputChange}/>
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li>{todo}</li>
))}
</ul>
</div>
)
}
export default App;
Map loops through and returns JSX for each item.
As our app grows more complex, we'll want to split it into multiple components. Components help manage reusable pieces of UI separately.
For example, we could extract the todo list items into their own component TodoItem.js
:
// TodoItem.js
function TodoItem({ todo }) {
return <li>{todo}</li>
}
export default TodoItem;
Now in our App.js
component:
// App.js
import TodoItem from "./TodoItem.js";
function App() {
//...
return (
//...
{todos.map(todo => (
<TodoItem key={todo} todo={todo} />
))}
//...
)
}
export default App;
We pass the individual todo as a prop. Props allow parent/child components to communicate.
Props are read-only - a component receiving props can't mutate them. To allow interaction like deleting an item, we also pass a handler prop:
// TodoItem.js
import { useState } from 'react';
function TodoItem({todo, todos, setTodos}) {
const handleDelete = () => {
setTodos(todos.filter(t => t !== todo));
}
return (
<li>
{todo}
<button onClick={handleDelete}>
Delete
</button>
</li>
);
}
export default TodoItem;
Now the <TodoItem>
can call props.setTodos() on click.
This separation of concerns makes our code cleaner, reusable, and more scalable as the app grows.
Here is the complete code for our React To-Do List App:
// App.js
import { useState } from 'react';
import TodoItem from './TodoItem';
function App() {
const [todos, setTodos] = useState([]);
const [newTodo, setNewTodo] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
setTodos([...todos, newTodo]);
setNewTodo('');
};
const handleChange = (e) => {
setNewTodo(e.target.value);
};
return (
<div>
<h1>To-Do List</h1>
<input type="text" placeholder="Enter a task" value={newTodo} onChange={handleInputChange}/>
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<TodoItem
key={todo}
todo={todo}
todos={todos}
setTodos={setTodos}
/>
))}
</ul>
</div>
);
}
export default App;
// TodoItem.js
import { useState } from 'react';
function TodoItem({todo, todos, setTodos}) {
const handleDelete = () => {
setTodos(todos.filter(t => t !== todo));
}
return (
<li>
{todo}
<button onClick={handleDelete}>
Delete
</button>
</li>
);
}
export default TodoItem;
And that's it - a basic but fully functional To-Do List App built with React! I hope this step-by-step tutorial has given you a good understanding of core React concepts like components, props, state and how to structure a simple app. Moving forward, you can continue adding features and styles to expand it further.
Conclusion
In this article, we explored React, a popular frontend framework. We covered key concepts like the virtual DOM, components, state, and props. Part 1 laid the foundation for our journey into React.
In Part 2, we'll delve into JSX, hooks, and popular libraries. Stay tuned for more insights on mastering React and advancing your frontend development skills. Happy coding!