React_Code_QA
Todo List
import { useState } from 'react';
let id = 0;
const INITIAL_TASKS = [
{ id: id++, label: 'Walk the dog' },
{ id: id++, label: 'Water the plants' },
{ id: id++, label: 'Wash the dishes' },
];
export default function App() {
const [tasks, setTasks] = useState(INITIAL_TASKS);
const [newTask, setNewTask] = useState('');
return (
<div>
<h1>Todo List</h1>
<div>
<input
aria-label="Add new task"
type="text"
placeholder="Add your task"
value={newTask}
onChange={(event) => {
setNewTask(event.target.value);
}}
/>
<div>
<button
onClick={() => {
setTasks(
tasks.concat({
id: id++,
label: newTask.trim(),
}),
);
setNewTask('');
}}>
Submit
</button>
</div>
</div>
<ul>
{tasks.map(({ id, label }) => (
<li key={id}>
<span>{label}</span>
<button
onClick={() => {
setTasks(
tasks.filter((task) => task.id !== id),
);
}}>
Delete
</button>
</li>
))}
</ul>
</div>
);
}
Tabs
import { useState } from 'react';
export default function Tabs({ defaultValue, items }) {
const [value, setValue] = useState(
defaultValue ?? items[0].value,
);
return (
<div className="tabs">
<div className="tabs-list">
{items.map(({ label, value: itemValue }) => {
const isActiveValue = itemValue === value;
return (
<button
key={itemValue}
type="button"
className={[
'tabs-list-item',
isActiveValue && 'tabs-list-item--active',
]
.filter(Boolean)
.join(' ')}
onClick={() => {
setValue(itemValue);
}}>
{label}
</button>
);
})}
</div>
<div>
{items.map(({ panel, value: itemValue }) => (
<div key={itemValue} hidden={itemValue !== value}>
{panel}
</div>
))}
</div>
</div>
);
}
body {
font-family: sans-serif;
}
.wrapper {
align-items: center;
display: flex;
}
.tabs {
display: flex;
flex-direction: column;
gap: 8px;
}
.tabs-list {
display: flex;
gap: 6px;
}
.tabs-list-item {
--active-color: blueviolet;
background: none;
border: 1px solid #000;
border-radius: 4px;
cursor: pointer;
padding: 6px 10px;
}
.tabs-list-item:hover {
border-color: var(--active-color);
color: var(--active-color);
}
.tabs-list-item--active,
.tabs-list-item--active:hover {
border-color: var(--active-color);
background-color: var(--active-color);
color: #fff;
}
Temperature Converter
import { useState } from 'react';
function format(number) {
// Show 4 d.p. if number has more than 4 decimal places.
return /\.\d{5}/.test(number)
? Number(number).toFixed(4)
: number;
}
export default function App() {
const [celsius, setCelsius] = useState('');
const [fahrenheit, setFahrenheit] = useState('');
function convert(value, setDestination, calculateValue) {
const numericValue = Number(value);
const isValid =
!Number.isNaN(numericValue) && Boolean(value);
setDestination(
isValid ? format(calculateValue(numericValue)) : '',
);
}
return (
<div>
<div className="temperature-converter">
{/* Use a label for better a11y. */}
<label className="temperature-converter-column">
<input
className="temperature-converter-column-top-row"
type="number"
value={celsius}
onChange={(event) => {
const newValue = event.target.value;
setCelsius(newValue);
convert(
newValue,
setFahrenheit,
(value) => (value * 9) / 5 + 32,
);
}}
/>
<div className="temperature-converter-column-bottom-row">
Celsius
</div>
</label>
<div className="temperature-converter-column">
<div className="temperature-converter-column-top-row">
=
</div>
</div>
{/* Use a label for better a11y. */}
<label className="temperature-converter-column">
<input
className="temperature-converter-column-top-row"
type="number"
value={fahrenheit}
onChange={(event) => {
const newValue = event.target.value;
setFahrenheit(newValue);
convert(
newValue,
setCelsius,
(value) => ((value - 32) * 5) / 9,
);
}}
/>
<div className="temperature-converter-column-bottom-row">
Fahrenheit
</div>
</label>
</div>
</div>
);
}
Job Board
import { useEffect, useRef, useState } from 'react';
import JobPosting from './JobPosting';
const PAGE_SIZE = 6;
export default function App() {
const [fetchingJobDetails, setFetchingJobDetails] =
useState(false);
const [page, setPage] = useState(0);
const [jobIds, setJobIds] = useState(null);
const [jobs, setJobs] = useState([]);
const isMounted = useRef(true);
useEffect(() => {
isMounted.current = true;
// Indicate that the component is unmounted, so
// that requests that complete after the component
// is unmounted don't cause a "setState on an unmounted
// component error".
return () => {
isMounted.current = false;
};
}, []);
useEffect(() => {
fetchJobs(page);
}, [page]);
async function fetchJobIds(currPage) {
let jobs = jobIds;
if (!jobs) {
const res = await fetch(
'https://hacker-news.firebaseio.com/v0/jobstories.json',
);
jobs = await res.json();
// No-op if component is unmounted.
if (!isMounted.current) {
return;
}
setJobIds(jobs);
}
const start = currPage * PAGE_SIZE;
const end = start + PAGE_SIZE;
return jobs.slice(start, end);
}
async function fetchJobs(currPage) {
const jobIdsForPage = await fetchJobIds(currPage);
setFetchingJobDetails(true);
const jobsForPage = await Promise.all(
jobIdsForPage.map((jobId) =>
fetch(
`https://hacker-news.firebaseio.com/v0/item/${jobId}.json`,
).then((res) => res.json()),
),
);
// No-op if component is unmounted.
if (!isMounted.current) {
return;
}
setFetchingJobDetails(false);
// useEffect (and hence `fetchJobs`) runs twice on component mount
// during development in Strict Mode.
//
// But since the value of `jobs` within the closure is the same,
// the resulting combined jobs will be the same, assuming the results
// for the API stays the same between requests.
const combinedJobs = [...jobs, ...jobsForPage];
setJobs(combinedJobs);
}
return (
<div className="app">
<h1 className="title">Hacker News Jobs Board</h1>
{jobIds == null ? (
<p className="loading">Loading...</p>
) : (
<div>
<div className="jobs" role="list">
{jobs.map((job) => (
<JobPosting key={job.id} {...job} />
))}
</div>
{jobs.length > 0 &&
page * PAGE_SIZE + PAGE_SIZE <
jobIds.length && (
<button
className="load-more-button"
disabled={fetchingJobDetails}
onClick={() => setPage(page + 1)}>
{fetchingJobDetails
? 'Loading...'
: 'Load more jobs'}
</button>
)}
</div>
)}
</div>
);
}
export default function JobPosting({
url,
by,
time,
title,
}) {
return (
<div className="post" role="listitem">
<h2 className="post__title">
{url ? (
<a href={url} target="_blank" rel="noopener">
{title}
</a>
) : (
title
)}
</h2>
<p className="post__metadata">
By {by} ·{' '}
{new Date(time * 1000).toLocaleString()}
</p>
</div>
);
}
* {
margin: 0;
padding: 0;
}
body {
background-color: #f6f6ef;
color: #000;
font-family: sans-serif;
font-size: 16px;
padding: 16px;
}
a {
color: inherit;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.app {
max-width: 600px;
margin: 0 auto;
}
.title {
font-size: 24px;
font-weight: bold;
color: #ff6600;
margin-bottom: 24px;
}
.jobs {
display: grid;
row-gap: 16px;
}
.post {
background-color: #fff;
border: 1px solid #dcdcdc;
border-radius: 4px;
padding: 16px;
display: grid;
row-gap: 8px;
}
.post__title {
font-size: 16px;
font-weight: bold;
margin-top: 0;
}
.post__metadata {
font-size: 14px;
color: #444;
}
.loading {
color: #4d4d4d;
font-weight: bold;
font-size: 18px;
}
.load-more-button {
background-color: #ff6600;
border: none;
border-radius: 4px;
color: #fff;
margin-top: 20px;
padding: 8px 12px;
}
.load-more-button:not(:disabled) {
cursor: pointer;
}
.load-more-button:hover {
background-color: #e65c00;
}
API Call in React
- async function
await fetch
await response.text()
- request
- method
- heads
'Content-Type': 'application/json',
- body
JSON.stringify({
name: 'John',
age: 30,
}),
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
async function fetchData() {
try{
setIsLoading(true);
setError(null);
const response = await fetch('https://api.example.com/data',{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'John',
age: 30,
}),
});
if(response.ok){
setLiked(!liked)
}
const data = await response.json();
return data;
}catch(error){
setError(error);
console.error('Error fetching data:', error);
} finally {
setIsLoading(false);
}
const data = await response.json();
return data;
}