Refactor with React hooks

This commit is contained in:
Damillora 2021-03-29 14:40:58 +07:00
parent e04d91587b
commit 297a732676
6 changed files with 138 additions and 183 deletions

View File

@ -20,4 +20,15 @@ export interface ProductFormState {
}, },
errors: string[], errors: string[],
busy: boolean, busy: boolean,
}
export function validateForm(form): string[] {
let errors: string[] = [];
if (!form.name) {
errors.push("Name required");
}
if (!form.price) {
errors.push("Price required");
}
return errors;
} }

View File

@ -1,66 +1,48 @@
import React, { ChangeEvent } from 'react'; import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import { Layout } from '../shared/Layout'; import { Layout } from '../shared/Layout';
import { ProductClient } from '../../APIClient'; import { ProductClient } from '../../APIClient';
import Swal from 'sweetalert2'; import Swal from 'sweetalert2';
import ProductForm from '../../components/ProductForm'; import ProductForm from '../../components/ProductForm';
import Errors from '../../components/Errors';
import Link from 'next/link'; import Link from 'next/link';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'; import { faArrowLeft } from '@fortawesome/free-solid-svg-icons';
import { ProductFormState } from '../../interfaces/product'; import { validateForm } from '../../lib/product';
class CreateProduct extends React.Component<{}, ProductFormState> { function Errors({ errors }) {
constructor(props) { return errors.map(x => (
super(props) <li>{x}</li>
this.state = { ));
form: { }
name: "",
price: 0, function CreateProduct() {
}, let [form, setForm] = useState({ name: "", price: 0 });
errors: [], let [busy, setBusy] = useState(false);
busy: false, let [errors,setErrors] = useState([""]);
}
} const onNameChanged = (newName: string) => {
onNameChanged(newName) { setForm(
this.setState({ {
form: {
name: newName, name: newName,
price: this.state.form.price, price: form.price,
} }
);
}
const onPriceChanged = (newPrice: number) => {
setForm({
name: form.name,
price: newPrice,
}); });
} }
onPriceChanged(newPrice) { const validate = () => {
this.setState({ let errors: string[] = validateForm(form);
form: { setErrors(errors);
name: this.state.form.name,
price: newPrice
}
});
}
async validate() {
let errors: string[] = [];
if (!this.state.form.name) {
errors.push("Name required");
}
if (!this.state.form.price) {
errors.push("Price required");
}
await new Promise((resolve) => {
this.setState({
errors
}, () => resolve(undefined));
})
return errors.length == 0; return errors.length == 0;
} }
async onSubmit() { const onSubmit = async () => {
if (await this.validate()) { if (validate()) {
await new Promise((resolve) => { setBusy(true);
this.setState({
busy: true,
}, () => resolve(undefined));
})
try { try {
const form = this.state.form;
const client = new ProductClient(); const client = new ProductClient();
await client.post({ await client.post({
name: form.name, name: form.name,
@ -68,44 +50,37 @@ class CreateProduct extends React.Component<{}, ProductFormState> {
}) })
Swal.fire({ Swal.fire({
title: "Submitted!", title: "Submitted!",
text: "Product is now in database", text: "Product is added in database",
}) })
} catch (error) { } catch (error) {
} finally { } finally {
await new Promise((resolve) => { setBusy(false);
this.setState({
busy: false,
}, () => resolve(undefined));
})
} }
} }
} }
render() {
return (
<div className="container mx-auto">
<h1>Create Product</h1>
<Link href="/product/">
<a>
<FontAwesomeIcon icon={faArrowLeft} /> Return to index
</a>
</Link>
<ProductForm
name={this.state.form.name}
price={this.state.form.price}
busy={this.state.busy}
onNameChange={this.onNameChanged.bind(this)}
onPriceChange={this.onPriceChanged.bind(this)}
onSubmit={this.onSubmit.bind(this)} />
<ul>
<Errors errors={this.state.errors}></Errors>
</ul>
</div>
)
}
}
return (
<div className="container mx-auto">
<h1>Create Product</h1>
<Link href="/product/">
<a>
<FontAwesomeIcon icon={faArrowLeft} /> Return to index
</a>
</Link>
<ProductForm
name={form.name}
price={form.price}
busy={busy}
onNameChange={onNameChanged}
onPriceChange={onPriceChanged}
onSubmit={onSubmit} />
<ul>
<Errors errors={errors}></Errors>
</ul>
</div>
);
}
export default function CreateProductPage() { export default function CreateProductPage() {
return ( return (

View File

@ -1,5 +1,5 @@
import React, { ChangeEvent } from 'react'; import React, { useEffect, useState } from 'react';
import { withRouter, NextRouter, useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { Layout } from '../../shared/Layout'; import { Layout } from '../../shared/Layout';
import { ProductClient } from '../../../APIClient'; import { ProductClient } from '../../../APIClient';
import Swal from 'sweetalert2'; import Swal from 'sweetalert2';
@ -7,90 +7,69 @@ import ProductForm from '../../../components/ProductForm';
import Link from 'next/link'; import Link from 'next/link';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'; import { faArrowLeft } from '@fortawesome/free-solid-svg-icons';
import { ProductDataProps, ProductFormState } from '../../../interfaces/product'; import { validateForm } from '../../../lib/product';
function Errors({ errors }) { function Errors({ errors }) {
return errors.map(x => ( return errors.map(x => (
<li>{x}</li> <li>{x}</li>
)); ));
} }
class EditProduct extends React.Component<ProductDataProps,ProductFormState> {
constructor(props) { function EditProduct({ productID }) {
super(props) if (typeof productID != "string") {
const id = props.productID; return (
this.state = { <div>
form: { </div>
name: "", )
price: 0,
},
errors: [],
busy: false,
}
} }
async componentDidMount() { let [form, setForm] = useState({ name: "", price: 0 });
let [busy, setBusy] = useState(false);
let [errors,setErrors] = useState([""]);
const getInitialData = async() => {
const client = new ProductClient(); const client = new ProductClient();
await new Promise((resolve) => { let data = await client.get(productID);
this.setState({ setBusy(false);
busy: true, setForm(
}, () => resolve(undefined)); {
}) name: data.name,
if (this.props.productID) { price: data.price,
let data = await client.get(this.props.productID); }
await new Promise((resolve) => { );
this.setState({
busy: false,
}, () => resolve(undefined));
})
this.setState({
form: {
name: data.name,
price: data.price,
}
});
}
} }
onNameChanged(newName: string) { // Startup
this.setState({ useEffect(() => {
form: { setBusy(true);
if (productID) {
getInitialData();
}
}, [productID]);
const onNameChanged = (newName: string) => {
setForm(
{
name: newName, name: newName,
price: this.state.form.price, price: form.price,
} }
);
}
const onPriceChanged = (newPrice: number) => {
setForm({
name: form.name,
price: newPrice,
}); });
} }
onPriceChanged(newPrice: number) { const validate = () => {
this.setState({ let errors: string[] = validateForm(form);
form: { setErrors(errors);
name: this.state.form.name,
price: newPrice,
}
});
}
async validate() {
let errors: string[] = [];
if (!this.state.form.name) {
errors.push("Name required");
}
if (!this.state.form.price) {
errors.push("Email required");
}
await new Promise((resolve) => {
this.setState({
errors
}, () => resolve(undefined));
})
return errors.length == 0; return errors.length == 0;
} }
async onSubmit() { const onSubmit = async () => {
if (await this.validate()) { if (validate()) {
await new Promise((resolve) => { setBusy(true);
this.setState({
busy: true,
}, () => resolve(undefined));
})
try { try {
const form = this.state.form;
const client = new ProductClient(); const client = new ProductClient();
await client.put(this.props.productID, { await client.put(productID, {
name: form.name, name: form.name,
price: form.price, price: form.price,
}) })
@ -101,43 +80,31 @@ class EditProduct extends React.Component<ProductDataProps,ProductFormState> {
} catch (error) { } catch (error) {
} finally { } finally {
await new Promise((resolve) => { setBusy(false);
this.setState({
busy: false,
}, () => resolve(undefined));
})
} }
} }
} }
render() {
if (typeof this.props.productID != "string") { return (
return ( <div className="container mx-auto">
<div> <h1>Edit Product</h1>
</div> <Link href="/product/">
) <a>
} <FontAwesomeIcon icon={faArrowLeft} /> Return to index
return (
<div className="container mx-auto">
<h1>Edit Product</h1>
<Link href="/product/">
<a>
<FontAwesomeIcon icon={faArrowLeft} /> Return to index
</a> </a>
</Link> </Link>
<ProductForm <ProductForm
name={this.state.form.name} name={form.name}
price={this.state.form.price} price={form.price}
busy={this.state.busy} busy={busy}
onNameChange={this.onNameChanged.bind(this)} onNameChange={onNameChanged}
onPriceChange={this.onPriceChanged.bind(this)} onPriceChange={onPriceChanged}
onSubmit={this.onSubmit.bind(this)} /> onSubmit={onSubmit} />
<ul> <ul>
<Errors errors={this.state.errors}></Errors> <Errors errors={errors}></Errors>
</ul> </ul>
</div> </div>
) );
}
} }
export default function EditProductPage() { export default function EditProductPage() {

View File

@ -7,7 +7,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faEdit, faTrash } from '@fortawesome/free-solid-svg-icons'; import { faEdit, faTrash } from '@fortawesome/free-solid-svg-icons';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import Link from 'next/link'; import Link from 'next/link';
import { DeleteProductProps, ProductDataProps, ProductListRowsProps, ProductState } from '../../interfaces/product'; import { DeleteProductProps, ProductDataProps, ProductListRowsProps, ProductState } from '../../lib/product';
const DeleteProductButton: React.FunctionComponent<DeleteProductProps> = ({ productID, onDelete }) => { const DeleteProductButton: React.FunctionComponent<DeleteProductProps> = ({ productID, onDelete }) => {
const onClickDelete = async () => { const onClickDelete = async () => {

View File

@ -18,7 +18,7 @@ const NavLink: React.FunctionComponent<{href: string}> = ({href, children}) => {
if(active) { if(active) {
return "page"; return "page";
} else { } else {
return null; return undefined;
} }
} }
const active = router.pathname == href; const active = router.pathname == href;
@ -26,7 +26,7 @@ const NavLink: React.FunctionComponent<{href: string}> = ({href, children}) => {
return ( return (
<li className='nav-item active'> <li className='nav-item active'>
<Link href={href}> <Link href={href}>
<a className={NavLinkClasses(active)} aria-current={NavLinkCurrent}>{children}</a> <a className={NavLinkClasses(active)} aria-current={NavLinkCurrent(active)}>{children}</a>
</Link> </Link>
</li> </li>
); );
@ -53,6 +53,8 @@ const NavBar: React.FunctionComponent<{}> = () => {
<ul className='navbar-nav mr-auto'> <ul className='navbar-nav mr-auto'>
<NavLink href="/">Home</NavLink> <NavLink href="/">Home</NavLink>
<NavLink href="/todo">Todo</NavLink> <NavLink href="/todo">Todo</NavLink>
<NavLink href="/customer">Customer</NavLink>
<NavLink href="/product">Product</NavLink>
</ul> </ul>
</div> </div>
</nav> </nav>

Binary file not shown.