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

@ -21,3 +21,14 @@ export interface ProductFormState {
errors: string[],
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 { ProductClient } from '../../APIClient';
import Swal from 'sweetalert2';
import ProductForm from '../../components/ProductForm';
import Errors from '../../components/Errors';
import Link from 'next/link';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons';
import { ProductFormState } from '../../interfaces/product';
import { validateForm } from '../../lib/product';
class CreateProduct extends React.Component<{}, ProductFormState> {
constructor(props) {
super(props)
this.state = {
form: {
name: "",
price: 0,
},
errors: [],
busy: false,
}
}
onNameChanged(newName) {
this.setState({
form: {
function Errors({ errors }) {
return errors.map(x => (
<li>{x}</li>
));
}
function CreateProduct() {
let [form, setForm] = useState({ name: "", price: 0 });
let [busy, setBusy] = useState(false);
let [errors,setErrors] = useState([""]);
const onNameChanged = (newName: string) => {
setForm(
{
name: newName,
price: this.state.form.price,
price: form.price,
}
);
}
const onPriceChanged = (newPrice: number) => {
setForm({
name: form.name,
price: newPrice,
});
}
onPriceChanged(newPrice) {
this.setState({
form: {
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));
})
const validate = () => {
let errors: string[] = validateForm(form);
setErrors(errors);
return errors.length == 0;
}
async onSubmit() {
if (await this.validate()) {
await new Promise((resolve) => {
this.setState({
busy: true,
}, () => resolve(undefined));
})
const onSubmit = async () => {
if (validate()) {
setBusy(true);
try {
const form = this.state.form;
const client = new ProductClient();
await client.post({
name: form.name,
@ -68,44 +50,37 @@ class CreateProduct extends React.Component<{}, ProductFormState> {
})
Swal.fire({
title: "Submitted!",
text: "Product is now in database",
text: "Product is added in database",
})
} catch (error) {
} finally {
await new Promise((resolve) => {
this.setState({
busy: false,
}, () => resolve(undefined));
})
setBusy(false);
}
}
}
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() {
return (

View File

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

View File

@ -7,7 +7,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faEdit, faTrash } from '@fortawesome/free-solid-svg-icons';
import { useRouter } from 'next/router';
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 onClickDelete = async () => {

View File

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

Binary file not shown.