🥹React Api Typescript Step By Step (ok)
https://github.com/phamngoctuong/react-api
Last updated
Was this helpful?
https://github.com/phamngoctuong/react-api
Last updated
Was this helpful?
src\index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import "./index.css";
import App from './App';
import reportWebVitals from './reportWebVitals';
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import { thunk } from 'redux-thunk';
import MyReducers from './reducers';
declare global {
interface Window {
__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose;
}
}
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const middlewares:any[] = [
thunk
];
var store = createStore(
MyReducers,
composeEnhancers(applyMiddleware(...middlewares))
);
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
reportWebVitals();
src\App.tsx
import React, { Component } from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import routes from './routes';
import Menu from './components/MenuComponent';
class App extends Component {
showContentMenus = (routes: any) => {
var result = null;
if (routes.length > 0) {
result = routes.map((route: any, index: number) => {
return (
<Route
key={index}
path={route.path}
Component={route.main}
>
</Route>
)
})
}
return <Routes>{result}</Routes>;
}
render() {
return (
<BrowserRouter>
<div className="App">
<Menu />
{this.showContentMenus(routes)}
</div>
</BrowserRouter>
);
}
}
export default App;
src\components\MenuComponent.tsx
import { Component } from 'react';
import { Link } from 'react-router-dom';
class MenuComponent extends Component {
render() {
return (
<nav className="navbar navbar-expand-sm bg-dark navbar-dark">
<div className="collapse navbar-collapse" id="collapsibleNavId">
<ul className="navbar-nav mr-auto mt-2 mt-lg-0">
<li className="nav-item active">
<Link to="" className="nav-link">Home</Link>
</li>
<li className="nav-item">
<Link to="/product-list" className="nav-link">Quản lý sản phẩm</Link>
</li>
</ul>
</div>
</nav>
);
}
}
export default MenuComponent;
src\Pages\HomePage.tsx
import React, { Component } from 'react';
class HomePage extends Component {
render() {
return (
<div>
Home
</div>
);
}
}
export default HomePage;
src\Pages\ProductListPage.tsx
import React, { Component } from 'react';
class ProductListPage extends Component {
render() {
return (
<div>
ProductListPage
</div>
);
}
}
export default ProductListPage;
src\reducers\index.tsx
import { combineReducers } from 'redux';
import products from './productsReducer';
var MyReducers = combineReducers({
products
});
export default MyReducers;
src\reducers\productsReducer.tsx
var initialState = [
{
id: 1,
name: "Iphone 5 Plus",
price: 500,
status: true,
},
{
id: 2,
name: "Iphone 5 Plus",
price: 500,
status: false,
},
{
id: 3,
name: "Iphone 6 Plus",
price: 600,
status: false,
},
];
var products = (state = initialState, action: any) => {
switch (action.type) {
default:
return [...state];
break;
}
};
export default products;
src\routes\index.tsx
import HomePage from "../Pages/HomePage";
import ProductListPage from "../Pages/ProductListPage";
const routes = [
{
path: "/",
exact: true,
main: () => <HomePage />
},
{
path: '/product-list',
exact: false,
main: () => <ProductListPage />
},
];
export default routes;
src\actions\index.tsx
import * as actionConstant from "../constants/actionConstant";
import apiCaller from "../utils/apiCallerUtil";
export const actFetchProductsRequest = () => {
return (dispatch:any) => {
return apiCaller('products', 'GET', {}).then((res:any) => {
dispatch(actFetchProducts(res.data));
});
};
}
export const actFetchProducts = (products:any) => {
return {
type: actionConstant.FETCH_PRODUCTS,
products
}
}
src\components\MenuComponent.tsx
import { Component } from 'react';
import { Link } from 'react-router-dom';
class MenuComponent extends Component {
render() {
return (
<nav className="navbar navbar-expand-sm bg-dark navbar-dark">
<div className="collapse navbar-collapse" id="collapsibleNavId">
<ul className="navbar-nav mr-auto mt-2 mt-lg-0">
<li className="nav-item active">
<Link to="" className="nav-link">Home</Link>
</li>
<li className="nav-item">
<Link to="/product-list" className="nav-link">Quản lý sản phẩm</Link>
</li>
</ul>
</div>
</nav>
);
}
}
export default MenuComponent;
src\components\ProductItemComponent.tsx
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class ProductItemComponent extends Component<any,any> {
render() {
var { product, index }: any = this.props;
var showStatus = product.status ? 'Còn hàng' : 'Hết hàng';
var classStatus = product.status ? 'primary' : 'warning';
return (
<tr>
<td>{index + 1}</td>
<td>{product.id}</td>
<td>{product.name}</td>
<td>{product.price}</td>
<td>
<span className={`badge badge-${classStatus}`}>{showStatus}</span>
</td>
<td>
<Link
className="badge badge-primary mr-1"
to={`product/${product.id}/edit`}
>Sửa</Link>
<span
className="badge badge-danger"
>Xoá</span>
</td>
</tr>
);
}
}
export default ProductItemComponent;
src\components\ProductListComponent.tsx
import React, { Component } from 'react';
class ProductListComponent extends Component<any,any> {
render() {
return (
<div>
<div className="card">
<div className="card-header">Danh sách các sản phẩm</div>
<div className="card-body">
<table className="table table-hover">
<thead>
<tr>
<th>STT</th>
<th>Mã</th>
<th>Tên</th>
<th>Giá</th>
<th>Trạng thái</th>
<th>Hành động</th>
</tr>
</thead>
<tbody>
{this.props.children}
</tbody>
</table>
</div>
</div>
</div>
);
}
}
export default ProductListComponent;
src\constants\actionConstant.tsx
export const FETCH_PRODUCTS = 'FETCH_PRODUCTS';
export const ADD_PRODUCT = 'ADD_PRODUCT';
export const UPDATE_PRODUCT = 'UPDATE_PRODUCT';
export const DELETE_PRODUCT = 'DELETE_PRODUCT';
export const EDIT_PRODUCT = 'EDIT_PRODUCT';
src\constants\configConstant.tsx
export const API_URL = 'http://localhost:5000';
src\Pages\HomePage.tsx
import React, { Component } from 'react';
class HomePage extends Component {
render() {
return (
<div>
Home
</div>
);
}
}
export default HomePage;
src\Pages\ProductListPage.tsx
import React, { Component } from 'react';
import ProductListComponent from '../components/ProductListComponent';
import ProductItemComponent from '../components/ProductItemComponent';
import apiCaller from '../utils/apiCallerUtil';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { actFetchProductsRequest } from '../actions';
class ProductListPage extends Component<any, any> {
showProducts = (products: any) => {
var result = null;
if (products.length > 0) {
result = products.map((product: any, index: number) => {
return (
<ProductItemComponent
key={index}
product={product}
index={index}
>
</ProductItemComponent>
)
})
}
return result;
};
componentDidMount() {
this.props.fetchAllProducts();
}
render() {
var { products } = this.props;
return (
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<Link to="/product/add" className="btn btn-info mb-10">
Thêm Sản Phẩm
</Link>
<ProductListComponent>
{this.showProducts(products)}
</ProductListComponent>
</div>
);
}
}
var mapStateToProps = (state: any) => {
return {
products: state.products
}
}
const mapDispatchToProps = (dispatch: any, props: any) => {
return {
fetchAllProducts: () => {
dispatch(actFetchProductsRequest());
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(ProductListPage);
src\reducers\index.tsx
import { combineReducers } from 'redux';
import products from './productsReducer';
var MyReducers = combineReducers({
products
});
export default MyReducers;
src\reducers\productsReducer.tsx
import * as actionConstant from "../constants/actionConstant";
var initialState = [
{
id: 1,
name: "Iphone 5 Plus",
price: 500,
status: true,
},
{
id: 2,
name: "Iphone 5 Plus",
price: 500,
status: false,
},
{
id: 3,
name: "Iphone 6 Plus",
price: 600,
status: false,
},
];
var products = (state = initialState, action: any) => {
switch (action.type) {
case actionConstant.FETCH_PRODUCTS:
state = action.products;
return [...state];
default:
return [...state];
break;
}
};
export default products;
src\routes\index.tsx
import HomePage from "../Pages/HomePage";
import ProductListPage from "../Pages/ProductListPage";
const routes = [
{
path: "/",
exact: true,
main: () => <HomePage />
},
{
path: '/product-list',
exact: false,
main: () => <ProductListPage />
}
];
export default routes;
src\utils\apiCallerUtil.tsx
import * as Config from './../constants/configConstant';
import axios from 'axios';
export default function apiCaller(endpoint:(string|number), method:string, body:object) {
return axios({
method: method,
url: `${Config.API_URL}/${endpoint}`,
data: body
}).catch(error => {
console.log(error);
});
}
src\App.tsx
import React, { Component } from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import routes from './routes';
import Menu from './components/MenuComponent';
class App extends Component {
showContentMenus = (routes: any) => {
var result = null;
if (routes.length > 0) {
result = routes.map((route: any, index: number) => {
return (
<Route
key={index}
path={route.path}
Component={route.main}
>
</Route>
)
})
}
return <Routes>{result}</Routes>;
}
render() {
return (
<BrowserRouter>
<div className="App">
<Menu />
{this.showContentMenus(routes)}
</div>
</BrowserRouter>
);
}
}
export default App;
src\index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import "./index.css";
import App from './App';
import reportWebVitals from './reportWebVitals';
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import { thunk } from 'redux-thunk';
import MyReducers from './reducers';
declare global {
interface Window {
__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose;
}
}
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const middlewares:any[] = [
thunk
];
var store = createStore(
MyReducers,
composeEnhancers(applyMiddleware(...middlewares))
);
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
reportWebVitals();
src\actions\index.tsx
import * as actionConstant from "../constants/actionConstant";
import apiCaller from "../utils/apiCallerUtil";
export const actFetchProductsRequest = () => {
return (dispatch:any) => {
return apiCaller('products', 'GET', {}).then((res:any) => {
dispatch(actFetchProducts(res.data));
});
};
}
export const actFetchProducts = (products:any) => {
return {
type: actionConstant.FETCH_PRODUCTS,
products
}
}
export const actDeleteProductRequest = (id:any) => {
return (dispatch:any) => {
return apiCaller(`products/${id}`, 'DELETE', {}).then(res => {
dispatch(actDeleteProduct(id));
})
}
}
export const actDeleteProduct = (id:any) => {
return {
type: actionConstant.DELETE_PRODUCT,
id
}
}
src\components\MenuComponent.tsx
import { Component } from 'react';
import { Link } from 'react-router-dom';
class MenuComponent extends Component {
render() {
return (
<nav className="navbar navbar-expand-sm bg-dark navbar-dark">
<div className="collapse navbar-collapse" id="collapsibleNavId">
<ul className="navbar-nav mr-auto mt-2 mt-lg-0">
<li className="nav-item active">
<Link to="" className="nav-link">Home</Link>
</li>
<li className="nav-item">
<Link to="/product-list" className="nav-link">Quản lý sản phẩm</Link>
</li>
</ul>
</div>
</nav>
);
}
}
export default MenuComponent;
src\components\ProductItemComponent.tsx
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class ProductItemComponent extends Component<any, any> {
onDelete = (id: number) => {
if (confirm('Bạn chắc chắn muốn xóa ?')) { //eslint-disable-line
this.props.onDelete(id);
}
}
render() {
var { product, index }: any = this.props;
var showStatus = product.status ? 'Còn hàng' : 'Hết hàng';
var classStatus = product.status ? 'primary' : 'warning';
return (
<tr>
<td>{index + 1}</td>
<td>{product.id}</td>
<td>{product.name}</td>
<td>{product.price}</td>
<td>
<span className={`badge badge-${classStatus}`}>{showStatus}</span>
</td>
<td>
<Link
className="badge badge-primary mr-1"
to={`product/${product.id}/edit`}
>Sửa</Link>
<button
type="button"
className="btn btn-danger"
onClick={() => this.onDelete(product.id)}
>
Xóa
</button>
</td>
</tr>
);
}
}
export default ProductItemComponent;
src\components\ProductListComponent.tsx
import React, { Component } from 'react';
class ProductListComponent extends Component<any,any> {
render() {
return (
<div>
<div className="card">
<div className="card-header">Danh sách các sản phẩm</div>
<div className="card-body">
<table className="table table-hover">
<thead>
<tr>
<th>STT</th>
<th>Mã</th>
<th>Tên</th>
<th>Giá</th>
<th>Trạng thái</th>
<th>Hành động</th>
</tr>
</thead>
<tbody>
{this.props.children}
</tbody>
</table>
</div>
</div>
</div>
);
}
}
export default ProductListComponent;
src\constants\actionConstant.tsx
export const FETCH_PRODUCTS = 'FETCH_PRODUCTS';
export const ADD_PRODUCT = 'ADD_PRODUCT';
export const UPDATE_PRODUCT = 'UPDATE_PRODUCT';
export const DELETE_PRODUCT = 'DELETE_PRODUCT';
export const EDIT_PRODUCT = 'EDIT_PRODUCT';
src\constants\configConstant.tsx
export const API_URL = 'http://localhost:5000';
src\Pages\HomePage.tsx
import React, { Component } from 'react';
class HomePage extends Component {
render() {
return (
<div>
Home
</div>
);
}
}
export default HomePage;
src\Pages\ProductListPage.tsx
import React, { Component } from 'react';
import ProductListComponent from '../components/ProductListComponent';
import ProductItemComponent from '../components/ProductItemComponent';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { actDeleteProductRequest, actFetchProductsRequest } from '../actions';
class ProductListPage extends Component<any, any> {
onDelete = (id: number) => {
this.props.onDeleteProduct(id);
}
showProducts = (products: any) => {
var result = null;
if (products.length > 0) {
result = products.map((product: any, index: number) => {
return (
<ProductItemComponent
key={index}
product={product}
index={index}
onDelete={this.onDelete}
>
</ProductItemComponent>
)
})
}
return result;
};
componentDidMount() {
this.props.fetchAllProducts();
}
render() {
var { products } = this.props;
return (
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<Link to="/product/add" className="btn btn-info mb-10">
Thêm Sản Phẩm
</Link>
<ProductListComponent>
{this.showProducts(products)}
</ProductListComponent>
</div>
);
}
}
var mapStateToProps = (state: any) => {
return {
products: state.products
}
}
const mapDispatchToProps = (dispatch: any, props: any) => {
return {
fetchAllProducts: () => {
dispatch(actFetchProductsRequest());
},
onDeleteProduct: (id: number) => {
dispatch(actDeleteProductRequest(id));
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(ProductListPage);
src\reducers\index.tsx
import { combineReducers } from 'redux';
import products from './productsReducer';
var MyReducers = combineReducers({
products
});
export default MyReducers;
src\reducers\productsReducer.tsx
import * as actionConstant from "../constants/actionConstant";
const _ = require('lodash');
var initialState = [
{
id: 1,
name: "Iphone 5 Plus",
price: 500,
status: true,
},
{
id: 2,
name: "Iphone 5 Plus",
price: 500,
status: false,
},
{
id: 3,
name: "Iphone 6 Plus",
price: 600,
status: false,
},
];
var products = (state = initialState, action: any) => {
var { id, product } = action;
var index;
switch (action.type) {
case actionConstant.FETCH_PRODUCTS:
state = action.products;
return [...state];
case actionConstant.DELETE_PRODUCT:
index = _.findIndex(state, function (o: any) {
return o.id == id;
});
state.splice(index, 1);
return [...state];
default:
return [...state];
break;
}
};
export default products;
src\routes\index.tsx
import HomePage from "../Pages/HomePage";
import ProductListPage from "../Pages/ProductListPage";
const routes = [
{
path: "/",
exact: true,
main: () => <HomePage />
},
{
path: '/product-list',
exact: false,
main: () => <ProductListPage />
}
];
export default routes;
src\utils\apiCallerUtil.tsx
import * as Config from './../constants/configConstant';
import axios from 'axios';
export default function apiCaller(endpoint:(string|number), method:string, body:object) {
return axios({
method: method,
url: `${Config.API_URL}/${endpoint}`,
data: body
}).catch(error => {
console.log(error);
});
}
src\App.tsx
import React, { Component } from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import routes from './routes';
import Menu from './components/MenuComponent';
class App extends Component {
showContentMenus = (routes: any) => {
var result = null;
if (routes.length > 0) {
result = routes.map((route: any, index: number) => {
return (
<Route
key={index}
path={route.path}
Component={route.main}
>
</Route>
)
})
}
return <Routes>{result}</Routes>;
}
render() {
return (
<BrowserRouter>
<div className="App">
<Menu />
{this.showContentMenus(routes)}
</div>
</BrowserRouter>
);
}
}
export default App;
src\index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import "./index.css";
import App from './App';
import reportWebVitals from './reportWebVitals';
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import { thunk } from 'redux-thunk';
import MyReducers from './reducers';
declare global {
interface Window {
__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose;
}
}
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const middlewares:any[] = [
thunk
];
var store = createStore(
MyReducers,
composeEnhancers(applyMiddleware(...middlewares))
);
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
reportWebVitals();
src\actions\index.tsx
import * as actionConstant from "../constants/actionConstant";
import apiCaller from "../utils/apiCallerUtil";
export const actFetchProductsRequest = () => {
return (dispatch:any) => {
return apiCaller('products', 'GET', {}).then((res:any) => {
dispatch(actFetchProducts(res.data));
});
};
}
export const actFetchProducts = (products:any) => {
return {
type: actionConstant.FETCH_PRODUCTS,
products
}
}
export const actDeleteProductRequest = (id:any) => {
return (dispatch:any) => {
return apiCaller(`products/${id}`, 'DELETE', {}).then(res => {
dispatch(actDeleteProduct(id));
})
}
}
export const actDeleteProduct = (id:any) => {
return {
type: actionConstant.DELETE_PRODUCT,
id
}
}
export const actAddProductRequest = (product:any) => {
return (dispatch:any) => {
return apiCaller('products', 'POST', product).then((res:any) => {
dispatch(actAddProduct(res.data));
});
}
}
export const actAddProduct = (product:any) => {
return {
type: actionConstant.ADD_PRODUCT,
product
}
}
src\Pages\ProductActionPage.tsx
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { actAddProductRequest } from '../actions';
import { connect } from 'react-redux';
var _ = require("lodash");
class ProductActionPage extends Component<any, any> {
constructor(props: any) {
super(props);
this.state = {
id: '',
txtName: '',
txtPrice: '',
chkbStatus: ''
};
}
onChange = (e: any) => {
var target = e.target;
var name = target.name;
var value = target.type === 'checkbox' ? target.checked : target.value;
this.setState({
[name]: value
});
}
onSave = (e: any) => {
e.preventDefault();
var { id, txtName, txtPrice, chkbStatus } = this.state;
var product = {
id: id,
name: txtName,
price: txtPrice,
status: chkbStatus
};
if (id) {
} else {
product.id = _.uniqueId;
this.props.onAddProduct(product);
}
}
render() {
var { txtName, txtPrice, chkbStatus } = this.state;
return (
<div className="col-xs-6 col-sm-6 col-md-6 col-lg-6">
<form onSubmit={this.onSave}>
<div className="form-group">
<label>Tên Sản Phẩm: </label>
<input
type="text"
className="form-control"
name="txtName"
value={txtName}
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label>Giá: </label>
<input
type="number"
className="form-control"
name="txtPrice"
value={txtPrice}
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label>Trạng Thái: </label>
</div>
<div className="checkbox">
<label>
<input
type="checkbox"
name="chkbStatus"
value={chkbStatus}
onChange={this.onChange}
checked={chkbStatus}
/>
Còn Hàng
</label>
</div>
<Link to="/product-list" className="btn btn-danger mr-10">
Trở Lại
</Link>
<button type="submit" className="btn btn-primary">Lưu Lại</button>
</form>
</div>
);
}
}
const mapStateToProps = (state:any) => {
return {
}
}
const mapDispatchToProps = (dispatch:any, props:any) => {
return {
onAddProduct: (product:any) => {
dispatch(actAddProductRequest(product));
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ProductActionPage);
src\reducers\productsReducer.tsx
import * as actionConstant from "../constants/actionConstant";
const _ = require('lodash');
var initialState = [
{
id: 1,
name: "Iphone 5 Plus",
price: 500,
status: true,
},
{
id: 2,
name: "Iphone 5 Plus",
price: 500,
status: false,
},
{
id: 3,
name: "Iphone 6 Plus",
price: 600,
status: false,
},
];
var products = (state = initialState, action: any) => {
var { id, product } = action;
var index;
switch (action.type) {
case actionConstant.FETCH_PRODUCTS:
state = action.products;
return [...state];
case actionConstant.DELETE_PRODUCT:
index = _.findIndex(state, function (o: any) {
return o.id == id;
});
state.splice(index, 1);
return [...state];
case actionConstant.ADD_PRODUCT:
state.push(action.product);
return [...state];
default:
return [...state];
break;
}
};
export default products;
src\routes\index.tsx
import HomePage from "../Pages/HomePage";
import ProductActionPage from "../Pages/ProductActionPage";
import ProductListPage from "../Pages/ProductListPage";
const routes = [
{
path: "/",
exact: true,
main: () => <HomePage />
},
{
path: '/product-list',
exact: false,
main: () => <ProductListPage />
},
{
path: '/product/add',
exact: false,
main: () => <ProductActionPage />
},
];
export default routes;
src\utils\apiCallerUtil.tsx
import * as Config from './../constants/configConstant';
import axios from 'axios';
export default function apiCaller(endpoint:(string|number), method:string, body:object) {
return axios({
method: method,
url: `${Config.API_URL}/${endpoint}`,
data: body
}).catch(error => {
console.log(error);
});
}