How to use this Tutorial:
Advanced Tutorial that requires some basic knowledge in:
Technical and installation requirements:
React is a JavaScript library for building user interfaces. It is a popular choice for Frontend frameworks.
TypeScript is a superset of JavaScript that adds static typing to the language. It is a popular choice for Frontend frameworks.
We will write the ReactTS app in TypeScript, as it is a more robust language than JavaScript.
There's a lot to learn about React and TypeScript, so I won't go into detail here. You can read more about React and TypeScript in the official docs.
We will use the create-react-app
generator to create the ReactTS app.
npx create-react-app [REACT_APP_NAME] --template typescript
npm install
to install the dependencies.env
file in the root directory
REACT_APP_API_URL=http://localhost:3000/api/v1
to the .env
file
PORT=3001
.env
to the .gitignore
file
.env
file from being pushed to GitHubnpm start
to start the ReactTS app
localhost:3001
We will use:
We're using TypeScript, so we will also need to install the types files for Bootstrap and Axios.
npm install react-bootstrap@next
npm install @types/react-bootstrap@next
npm install axios
npm install @types/axios
npm install react-router-dom
npm install @types/react-router-dom
These are the core dependencies we will use for the ReactTS app.
We will use have this folder structure:
grad_project
├── api # <-- Rails API app
├── client # <-- ReactTS app
│ ├── public
│ │ ├── index.html
│ │ └── ...
│ ├── src
│ │ ├── components
│ │ │ ├── [COMPONENT_NAME]
│ │ │ ├── ...
│ │ ├── pages
│ │ │ ├── [PAGE_NAME]
│ │ │ ├── ...
│ │ ├── Types
│ │ │ ├── [TS types files]
│ │ ├── App.css
│ │ ├── App.test.tsx
│ │ ├── App.tsx # <-- routes
│ │ ├── index.css
│ │ ├── index.tsx # <-- entry point
│ │ ├── react-app-env.d.ts
│ │ ├── reportWebVitals.ts
│ │ └── setupTests.ts
│ ├── .env
│ ├── .gitignore
│ ├── package.json
│ ├── README.md
│ └── tsconfig.json
The way that create-react-app
works is that it will create a public
and src
folder for you, with some boilerplate files.
index.tsx
is normally the entry point of the app (i.e. the first file that is run and the element with id=root
where the app renders). App.tsx
is also automatically generated and is the main component that is rendered in index.tsx
.
We will create a components
and pages
folder in the src
folder, as well as some other files.
components
folder in the src
folderpages
folder in the src
folder__mocks__
folder in the src
folder
__tests__
folder in the components
folder and pages
folder
For the routing, we will use React Router DOM.
App.tsx
file, add:import { Routes } from 'react-router-dom';
import { HomePage } from './pages/HomePage';
[...other imports like Navbar custom component]
export const App = () => {
return (
<>
<NavbarComponent />
<Routes>
<Route path='/' element={<HomePage />} />
</Routes>
</>
);
};
pages
folder, create a HomePage
fileHomePage
file, add:import React from 'react';
export const HomePage = () => {
return (
<div>
<h1>Home Page</h1>
</div>
);
};
When you run the app, you should see the Home Page.
You may want to reorganise the folder structure later on, e.g. move stylesheets to a styles
folder, but for now we will keep it simple. If you do move the stylesheets, for example, you will need to update the import
statements in the components and pages.
We will use Jest (official docs) for testing.
npm install jest
npm install @types/jest
jest.config.js
file in the root directory
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: [
'@testing-library/jest-dom/extend-expect'
],
transform: {
'^.+\.(js|jsx|ts|tsx)$': 'babel-jest',
},
};
jest.config.js
to the .gitignore
file
jest.config.js
file from being pushed to GitHub'test': 'jest --watchAll --verbose'
to the scripts
block in the package.json
fileWe will create the following components:
Navbar
PostList
PostItem
PostForm
Navbar
componentcomponents
folder, create a Navbar.tsx
fileNavbar.tsx
file, add:import React from 'react';
import { Navbar, Container, Nav } from 'react-bootstrap';
export const NavbarComponent = () => {
return (
<Navbar bg='dark' variant='dark'>
<Container>
<Navbar.Brand href='/'>Grad Project</Navbar.Brand>
<Nav className='me-auto'>
<Nav.Link href='/'>Home</Nav.Link>
</Nav>
</Container>
</Navbar>
);
};
App.tsx
file, add:import { NavbarComponent } from './components/Navbar';
// update the the App component with the NavbarComponent
export const App = () => {
return (
<>
<NavbarComponent />
<Routes>
<Route path='/' element={<HomePage />} />
</Routes>
</>
);
};
When you run the app, you should see the Navbar on the Home page.
PostList
componentcomponents
folder, create a PostList.tsx
filePostList.tsx
file, add:import React, { useState } from 'react';
export const PostList = () => {
const [posts, setPosts] = useState([]);
const getPosts = () => {
try {
const res = await axios.get(`${process.env.REACT_APP_API_URL}/posts`);
setPosts(res.data);
}
catch (err) {
console.log(err);
}
}
return (
<div>
<h1>Post List</h1>
<p># of posts: {posts.length}</p>
</div>
);
}
We will add the PostItem component later.
There's a lot to learn about state and hooks in React, so I won't go into detail here. You can read more about useState and useEffect in the official docs.
You may want to consider using useEfffect to fetch the data from the API based on certain criteria (e.g. page load, when some other state has been updated), but for now we will just use the getPosts
function.
PostItem
componentIn order to display the Post data, we will create a PostItem
component that will be iteratively rendered in the PostList
component.
components
folder, create a PostItem.tsx
filePostItem.tsx
file, add:import React, { useState } from 'react';
export const PostItem = ({ post }) => {
return (
<div>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
}
PostList.tsx
file, add:import { PostItem } from './PostItem';
export const PostList = () => {
// rest stays the same
// update the return statement to iteratively render the PostItem component
return (
<div>
<h1>Post List</h1>
<p># of posts: {posts.length}</p>
{posts.map((post) => (
<PostItem post={post} />
))}
</div>
);
}
We're not making the most of TypeScript here, as we're not using any types for the props. We will add this now.
Types
folder, create a PostTypes.ts
filePostTypes.ts
file, add:export interface Post {
id: number;
title: string;
content: string;
}
You will need to update this interface if you add more fields to the Post model.
PostItem.tsx
file, add:import { Post } from '../Types/PostTypes';
// add the type to the props
export const PostItem = ({ post }: { post: Post }) => {
// rest stays the same
}
TypeScript will now throw an error if you try to pass in a prop that is not of type Post
.
PostForm
componentWe will create a PostForm
component that will be used to create and update posts.
components
folder, create a PostForm.tsx
filePostForm.tsx
file, add:import React, { useState } from 'react';
export const PostForm = () => {
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
const post = {
title: title,
content: content,
};
console.log(post);
};
return (
<div>
<h1>Create Post</h1>
<form onSubmit={handleSubmit}>
<div className='mb-3'>
<label htmlFor='title' className='form-label'>
Title
</label>
<input
type='text'
className='form-control'
id='title'
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>
<div className='mb-3'>
<label htmlFor='content' className='form-label'>
Content
</label>
<textarea
className='form-control'
id='content'
rows={3}
value={content}
onChange={(e) => setContent(e.target.value)}
></textarea>
</div>
<button type='submit' className='btn btn-primary'>
Submit
</button>
</form>
</div>
);
};
Consider using react-hook-forms to handle form validation and submission. Find out more info here: https://react-hook-form.com/
You can opt to add a button to the PostList
component to open the PostForm
component in a modal, or you can create a separate PostCreate
component that will render the PostForm
component.
Alternatively, you can use react-router-dom to create a separate page for the PostForm
component in the pages
folder. You would add a route to the App.tsx
file and add a link to the PostForm
page.
PostList
component to the HomePage
pageAt the moment, the HomePage
page is just rendering a <h1>
tag.
We will add the PostList
component to the HomePage
page.
HomePage
file, add:import { PostList } from '../components/PostList';
export const HomePage = () => {
return (
<div>
<h1>Home Page</h1>
<PostList />
</div>
);
};
When you run the app, you should see the PostList
component rendered on the Home page.
Manually test the ReactTS app by running npm start
and checking the Home page.
You should see the PostList
component rendered on the Home page, and the number of posts in the database and each post title and content rendered.
You can also try creating Jest tests for the components in the __tests__
folder.
At this point, you should have:
PostList
component that renders the number of posts in the database and each post title and contentNavbar
PostList
PostItem
PostForm
Additionally, after manually testing the app, you should be able to see the PostList
component rendered on the Home page.
We still need to finish functionality for Creating, Updating and Deleting posts.
React
Frontend