INFO 253A: Front-end Web Architecture
Kay Ashaolu
Current State:
Limitations:
Databases:
Backend API:
Backend Technologies:
What is JSON Server?
db.json
fileBenefits:
Client-Server Communication:
HTTP Methods:
REST API:
Endpoint Examples:
GET /feedback
- Retrieve all feedback itemsGET /feedback/{id}
- Retrieve a specific itemPOST /feedback
- Add a new itemPUT /feedback/{id}
- Update an itemDELETE /feedback/{id}
- Delete an item1xx Informational:
2xx Success:
3xx Redirection:
4xx Client Errors:
5xx Server Errors:
Installation:
npm install json-server
Creating db.json
:
{
"feedback": [
{
"id": 1,
"rating": 10,
"text": "This is feedback item 1 coming from the backend"
},
{
"id": 2,
"rating": 9,
"text": "This is feedback item 2 coming from the backend"
},
{
"id": 3,
"rating": 8,
"text": "This is feedback item 3 coming from the backend"
}
]
}
Using Postman:
Example Requests:
GET Request:
http://localhost:5000/feedback
POST Request:
http://localhost:5000/feedback
{
"rating": 8,
"text": "New feedback item"
}
```
Problem:
Solution:
concurrently
packageInstallation:
npm install concurrently
Update package.json
:
"scripts": {
"server": "json-server --watch db.json --port 5000",
"client": "react-scripts start",
"dev": "concurrently \"npm run server\" \"npm run client\""
}
Using useEffect
Hook:
Example Code:
useEffect(() => {
fetch('/feedback?_sort=id&_order=desc')
.then(response => response.json())
.then(data => setFeedback(data));
}, []);
useState
to manage feedback data const [feedback, setFeedback] = useState([]);
isLoading
to track loading status const [isLoading, setIsLoading] = useState(true);
isLoading
to false
after data is fetched fetchData().then(() => setIsLoading(false));
isLoading
{isLoading ? <Spinner /> : <FeedbackList feedback={feedback} />}
fetch
with POST
method fetch('/feedback', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newFeedback)
})
.then(response => response.json())
.then(data => setFeedback([...feedback, data]));
Automatic ID Assignment:
fetch
with PUT
method fetch(`/feedback/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(updatedFeedback)
})
.then(response => response.json())
.then(data => {
// Update state with new data
});
fetch
with DELETE
method fetch(`/feedback/${id}`, { method: 'DELETE' })
.then(() => {
// Remove item from state
});
Holistic View:
Performance Optimization:
Scalability:
Client-Side (Frontend):
Server-Side (Backend):
Communication:
.current
property.import { useRef } from 'react';
function UseRefExample1() {
const inputRef = useRef();
const onSubmit = e => {
e.preventDefault();
console.log(inputRef.current.value);
inputRef.current.value = '';
inputRef.current.focus();
};
return (
<form onSubmit={onSubmit}>
<label>Name:</label>
<input type="text" ref={inputRef} />
<button type="submit">Submit</button>
</form>
);
}
export default UseRefExample1;
import { useState, useEffect, useRef } from 'react';
function UseRefExample2() {
const [name, setName] = useState('');
const prevName = useRef('');
useEffect(() => {
prevName.current = name;
}, [name]);
return (
<div>
<input
value={name}
onChange={e => setName(e.target.value)}
placeholder="Enter your name"
/>
<h2>Current Name: {name}</h2>
<h2>Previous Name: {prevName.current}</h2>
</div>
);
}
export default UseRefExample2;
import { useState, useEffect, useRef } from 'react';
function UseRefExample3() {
const [data, setData] = useState(null);
const isMounted = useRef(true);
useEffect(() => {
const fetchData = async () => {
try {
const res = await fetch('https://api.example.com/data');
if (isMounted.current) {
const result = await res.json();
setData(result);
}
} catch (error) {
console.error(error);
}
};
fetchData();
return () => {
isMounted.current = false;
};
}, []);
return <div>{data ? <div>{data.title}</div> : 'Loading...'}</div>;
}
export default UseRefExample3;
import { useState, useMemo } from 'react';
function UseMemoExample() {
const [number, setNumber] = useState(0);
const [inc, setInc] = useState(0);
const sqrt = useMemo(() => {
console.log('Expensive function called');
return Math.sqrt(number);
}, [number]);
const onClick = () => {
setInc(prevInc => prevInc + 1);
};
return (
<div>
<h2>Square Root of {number}: {sqrt}</h2>
<input
type="number" value={number}
onChange={e => setNumber(Number(e.target.value))}
/>
<button onClick={onClick}>Re-render</button>
<p>Renders: {inc}</p>
</div>
);
}
export default UseMemoExample;
// UseCallbackExample.js
import { useState, useCallback } from 'react';
import Button from './Button';
function UseCallbackExample() {
const [tasks, setTasks] = useState([]);
const addTask = useCallback(() => {
setTasks(prevTasks => [...prevTasks, 'New Task']);
}, [setTasks]);
return (
<div>
<Button addTask={addTask} />
{tasks.map((task, index) => (
<p key={index}>{task}</p>
))}
</div>
);
}
export default UseCallbackExample;
// Button.js
import React from 'react';
function Button({ addTask }) {
console.log('Button rendered');
return (
<button onClick={addTask}>Add Task</button>
);
}
export default React.memo(Button);
// useFetch.js
import { useState, useEffect } from 'react';
function useFetch(url, options) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortCont = new AbortController();
const fetchData = async () => {
try {
const res = await fetch(url, { ...options, signal: abortCont.signal });
if (!res.ok) throw new Error('Network response was not ok');
const json = await res.json();
setData(json);
setLoading(false);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err);
setLoading(false);
}
}
};
fetchData();
return () => abortCont.abort();
}, [url, options]);
return { data, loading, error };
}
export default useFetch;
// Usage in a component
import useFetch from './useFetch';
function CustomHookExample1() {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts', {});
if (loading) return <h3>Loading...</h3>;
if (error) return <h3>Error: {error.message}</h3>;
return (
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
export default CustomHookExample1;
// useLocalStorage.js
import { useState } from 'react';
function useLocalStorage(key, initialValue) {
const [localStorageValue, setLocalStorageValue] = useState(() => {
try {
const itemFromStorage = window.localStorage.getItem(key);
return itemFromStorage ? JSON.parse(itemFromStorage) : initialValue;
} catch (err) {
console.log(err);
return initialValue;
}
});
const setValue = value => {
try {
const valueToStore =
value instanceof Function ? value(localStorageValue) : value;
setLocalStorageValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (err) {
console.log(err);
}
};
return [localStorageValue, setValue];
}
export default useLocalStorage;