React JS crash course
Updated:
✒ Reference: The amazing React crash course by Traversy Media
What is React?
React JS is a frontend library/framework that structures the “view” layer of the application. It has reusable components with their own state, It enables dynamic markup using JSX, and also interactive UIs with Virtual DOM (allows update of parts of the page without reloading)
React - basic grammar
UI Components
Mindframe with React: when using React, think of your UI as a bunch of separate components.
// Function component
export const Header = () => {
return (
<div>
<h1>My Header</h1>
</div>
)
}
// Class component
export default class Header extends React.Component {
render() {
return (
<div>
<h1>My Header</h1>
</div>
)
}
}
Components render and return JSX (JavaScript Syntax Extension), and they can also take in props (attributes).
State
Components can have a “state” which is an object that determines how a component renders and behaves. (i.e. collapsable menu has an open and a close state)
Any data brought in can be a state, and it can be shared across multiple components.
“app” or “global” state: a state that is available to the entire UI, not just a single component
React Hooks
Hooks are functions that let us hook into the React state and lifecycle features from function components. You can also create your custom hooks!
-
useState - Returns a stateful value and a function to update it
-
useEffect - Perform side effects in function components
-
useContext, useReducer, useRef, etc.
Creating a React app
Use this link: Create a new react app
-
Install node.js and npm
- node: Link here
- npm:
npm install -g npm
at command line - Checking the versions:
npm -v
,node -v
-
Then create a React app:
npx create-react-app react-task-tracker
at terminal -
Start the dev server:
npm start
-
Open vs code to modify the project:
cd react-task-tracker
, thencode .
(Opens vs code in the current folder)- The web page auto-reloads whenever the files are changed!
Optional:
-
Adding react icons:
npm i react-icons
-
Adding the React Developer Tools chrome extension helps you inspect the React component hierarchies!
Exploring the created folder
package.json
- Dependencies include react (anything installed with npm will show here)
- “react-dom” is responsible for rendering the react application to the DOM model
- “react-scripts” comes with the development server
- “react-icons” comes with a package of react icons
- And some testing libraries
- Scripts
- “start”: run the development server
- “build”: build the app into static files for production
- “test”: start the test runner
- “eject”: remove the tool and copy build dependencies, configuration files and scripts into the app directory
public/index.html
-
The single page application that is being loaded
-
There is a
<div>
with id “root”: gateway to the UI
src/index.js
-
Entry point for React
-
ReactDOM.render()
InsertsApp
into<div>
-
App
comes fromApp.js
src/App.js
-
App is the “root” component
-
Everything in
index.html
is coded here
Expressions in JSX
We change the content of the page by changing App.js
:
// If x is true, then show 'true'; if not, show 'false'
function App() {
const name = 'Jen'
const x = true
return (
<div className="App">
<h1>Hello {name}</h1>
<h2>x is {x ? 'true' : 'false'}</h2>
</div>
);
}
- JSX return should always be a single parent element. Adding another
<div>
will give an error. The parent element can be an empty bracket: like<></>
. - JavaScript can be written directly inside the function.
- We can also use a class instead of a function:
import React from 'react'
class App extends React.Component {
render() {
return (
<h1>Hello from a class</h1>
)
}
}
Creating a component
Create a folder components
under scr
. You can create JS files here to use as components. (Make the first letter capital, it’s a convention!)
:memo: VS code extension ‘ES7 React/Redux/GraphQL/React-Native snippets’ allow snippets for React components!
rcc: Creates a class-based component rafce: Creates an arrow function with export (you can delete
import React from 'react'
) impt: Imports propTypes
Creating a Header
component and adding it to App.js
:
// Content of src/components/Header.js
const Header = () => {
return (
<header>
<h1>Task Tracker</h1>
</header>
)
}
export default Header;
// Content of src/App.js
// A header is added inside the container
import Header from './components/Header'
function App() {
return (
<div className="container">
<Header/>
</div>
);
}
export default App;
Component props
Props are used to pass data from a parent component to a child component. They are the main mechanism for component communication. (Just like function parameters)
// Content of src/components/Header.js
// The title is passed in as a prop
const Header = (props) => {
return (
<header>
<h1>{props.title}</h1>
</header>
)
}
export default Header
// Content of src/App.js
// The header now has a title
import Header from './components/Header'
function App() {
return (
<div className="container">
<Header title='Hello' />
</div>
);
}
export default App;
The title of the header (prop) is retrieved inside the component by {props.title}
.
Default prop
We can also add a default prop like below; If the header does not have any title, the default title will show. Adding a title will overwrite the default.
// Content of src/components/Header.js
// The default title of a header is now 'Task Tracker'
const Header = (props) => {
return (
<header>
<h1>{props.title}</h1>
</header>
)
}
Header.defaultProps = {
title: 'Task Tracker'
}
export default Header
Instead of passing in the props
object, we can destructure it and pass in the {title}
. Then we can add <h1>{title}</h1>
instead.
Prop types
You can set propTypes to make your code more robust. If the code below is added to Header.js
and the title of the header is 1) set to be of a different datatype or 2) undefined, the web page will render but the console will give you an error.
// Content of src/components/Header.js
Header.propTypes = {
title: PropTypes.string.isRequired,
}
Reusing props: Button Component
Once the button component is created, it can be reused by simply passing in different props.
// Content of src/components/Button.js
// This is a reusable button component
const Button = ({ color, text }) => {
return (
<button style=className='btn'>
{text}
</button>
)
}
export default Button
// Content of src/components/Header.js
// Different styles of buttons can be created with different props
import Button from './Button'
const Header = () => {
return (
<header className='header'>
<Button color='green' text='Hello'/>
<Button color='blue' text='Hello'/>
<Button color='red' text='Hello'/>
</header>
)
}
Styling
You can also add CSS within JS.
// Inline (double brackets)
// The style attribute accepts a JavaScript object with camelCased properties rather than a CSS string
<h1 style=>{title}</h1>
// Adding constants
<h1 style={headingStyle}>{title}</h1>
const headingStyle = {
color: 'red', backgroundColor: 'black'
}
States
Creating a Hook with useState
The code below takes the text
of each element in the tasks
array and creates a new Tasks
array with a <h3>
tag and a unique id, using .map()
.
// Content of src/components/Task.js
// The text of each task is shown in <h3>
const tasks = [
{
id: 1,
text: 'Doctors Appointment',
day: 'Feb 5th at 2:30pm',
reminder:true,
},
{
id: 2,
text: 'Meeting at School',
day: 'Feb 6th at 1:30pm',
reminder:true,
},
{
id: 3,
text: 'Food Shopping',
day: 'Feb 5th at 2:30pm',
reminder:false,
}
]
const Tasks = () => {
return (
<>
{tasks.map((task) => (
<h3 key={task.id}>{task.text}</h3>
))}
</>
)
}
Instead of having a constant out of the component, we can create a component state via useState()
.
// Content of src/components/Task.js
// The tasks are now states of the component
import { useState } from 'react'
const Tasks = () => {
const [tasks, setTasks] = useState([
{
id: 1,
text: 'Doctors Appointment',
day: 'Feb 5th at 2:30pm',
reminder:true,
},
{
id: 2,
text: 'Meeting at School',
day: 'Feb 6th at 1:30pm',
reminder:true,
},
{
id: 3,
text: 'Food Shopping',
day: 'Feb 5th at 2:30pm',
reminder:false,
}
])
return (
<>
{tasks.map((task) => (
<h3 key={task.id}>{task.text}</h3>
))}
</>
)
}
export default Tasks
A state cannot be changed within return()
because it is immutable. In order to change the task, we should use setTasks
.
Global States
Tasks should be global (should be accessible from other components too). They should not be inside Task.js
; they should be in App.js
, then passed in as props.
Making the tasks into app-level states:
// Content of src/App.js
// Tasks are now at top-level
import { useState } from 'react'
import Header from './components/Header'
import Tasks from './components/Tasks'
function App() {
const [tasks, setTasks] = useState([
{
id: 1,
text: 'Doctors Appointment',
day: 'Feb 5th at 2:30pm',
reminder:true,
},
{
id: 2,
text: 'Meeting at School',
day: 'Feb 6th at 1:30pm',
reminder:true,
},
{
id: 3,
text: 'Food Shopping',
day: 'Feb 5th at 2:30pm',
reminder:false,
}
])
// If there is no task left, show 'No Tasks To Show' instead of the Task components.
return (
<div className="container">
<Header />
{tasks.length > 0
? <Tasks tasks={tasks} onDelete={deleteTask}/>
: 'No Tasks To Show'}
</div>
);
}
export default App;
// Content of src/components/Tasks.js
// { tasks } are passed in as props
const Tasks = ({ tasks }) => {
return (
<>
{tasks.map((task) => (
<h3 key={task.id}>{task.text}</h3>
))}
</>
)
}
export default Tasks
Breaking down a component
Instead of having <h3>
tags for each task, we can create a Task
component which handles each task.
// Content of src/components/Tasks.js
// Each task is now a individual component
import Task from './Task'
const Tasks = ({ tasks }) => {
return (
<>
{tasks.map((task) => (
<Task key={task.id} task={task} />
))}
</>
)
}
export default Tasks
// Content of src/components/Task.js
// x icons are also added from the font awesome library
import { FaTimes } from 'react-icons/fa'
const Task = ({ task }) => {
return (
<div className='task'>
<h3>
{task.text}
<FaTimes style=/>
</h3>
<p>{task.day}</p>
</div>
)
}
export default Task
Event delegation
The function to (1) delete task and (2) toggle reminder should be within App.js
, and the function itself should be passed into Task.js
and Tasks.js
as props.
// Content of src/App.js
// Delete Task
const deleteTask = (id) => {
console.log("task deleted");
}
// Toggle Reminder
const toggleReminder = (id) => {
console.log("reminder toggled");
}
// When calling tasks in App.js, add the prop
<Tasks tasks={tasks} onDelete={deleteTask}
onToggle={toggleReminder} />
// Tasks.js sends the prop to Task.js
<Task key={task.id} task={task} onDelete={onDelete}
onToggle={onToggle} />
// In Task.js, the functions are called when the task div is double clicked or the x button is clicked
<div className='task' onDoubleClick={() => onToggle(task.id)}>
<h3>
{task.text}
<FaTimes
style=
onClick={() => onDelete(task.id)}
/>
</h3>
<p>{task.day}</p>
</div>
Dynamic change in components
Add a state showAddTask
to App.js
, and wrap <AddTask/>
with that state. If showAddTask
is true, then execute the code after &&
.
React allows you to make dynamic interfaces where no pages are being reloaded. Different styles can be chosen according to the state via {}
.
// in src/App.js
function App() {
const [showAddTask, setShowAddTask] = useState(false)
return (
<div className="container">
<Header onAdd={()=>setShowAddTask(!showAddTask)} showAdd={showAddTask}/>
{showAddTask && <AddTask onAdd={addTask}/>}
</div>
)
}
// in Header.js
const Header = ({title, onAdd, showAdd}) => {
return (
<header className='header'>
<h1>{title}</h1>
<Button
color={showAdd ? 'red' : 'green'}
text={showAdd ? 'Close' : 'Add'}
onClick={onAdd}
/>
</header>
)
}
Some JavaScript functions
- Filtering an array
Only leave the tasks where
task.id
is not equal toid
.
const deleteTask = (id) => {
setTasks(tasks.filter(task => task.id !== id))
}
- Changing the value of an object
If
task.id
is equal toid
, then change thetask.reminder
opposite to the current value.
const toggleReminder = (id) => {
setTasks(
tasks.map((task) =>
task.id === id ? { ...task, reminder: !task.reminder } : task)
)
}
- Setting class name according to a value
If
task.reminder
is true, then set class name asreminder
otherwise, set it astask
.
<div className={`task ${task.reminder ? 'reminder' : ''}`}
- Adding a task with a new id Add a random number as id, save the id of the new task, then add the new task as a state
// Add Task
const addTask = (task) => {
const id = Math.floor(Math.random()*10000) + 1
const newTask = { id, ...task }
setTasks([...tasks, newTask])
}
Building for production
Development build (local host 3000) is not optimized. We can use npm run build
to create a production build.
This creates an optimized production build, and the build folder contains the static assets of the production build. Files in /src
are the development stuff; files in /build
are the ones that are pushed to production and deployed.
npm install -g serve
creates a static http server. serve -s build
(serve -s build -p 8000
) serves the build. (at port 8000)
Leave a comment