🫢Tìm hiểu Saga thông qua một ví dụ (ok)
Last updated
Was this helpful?
Last updated
Was this helpful?
diện
package.json
{
"name": "pi",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mui/icons-material": "^6.4.1",
"@mui/material": "^6.4.1",
"@mui/styles": "^6.4.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"add": "^2.0.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-redux": "^9.2.0",
"react-scripts": "5.0.1",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"web-vitals": "^2.1.0",
"yarn": "^1.22.22"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
src\index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './containers/App';
import myReducer from './reducers/index';
import { thunk } from 'redux-thunk';
import { Provider } from 'react-redux';
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux'
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
var store = createStore(myReducer,composeEnhancer(applyMiddleware(thunk)));
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}><App /></Provider>
</React.StrictMode>
);
src\commons\Theme\index.js
import { createTheme } from '@mui/material/styles';
var theme = createTheme({
palette: {
secondary: {
main: '#123456'
}
},
borderradius: 4
});
export default theme;
src\constants\index.js
export const STATUSES = [
{
value: 0,
state: 'READY'
},
{
value: 1,
state: 'INPROCESSS'
},
{
value: 2,
state: 'COMPLATED'
}
];
src\containers\App\index.js
import React, { Component } from 'react';
import AddIcon from '@mui/icons-material/AddCircle';
import Container from '@mui/material/Container';
import TaskBoard from '../TaskBoard';
import Button from '@mui/material/Button';
class App extends Component {
render() {
return (
<Container maxWidth="lg">
<Button variant="contained"><AddIcon></AddIcon> Add</Button>
<TaskBoard></TaskBoard>
</Container>
);
}
}
export default App;
src\containers\TaskBoard\index.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
import ProductList from './../../components/ProductList';
import ProductItem from './../../components/ProductItem';
import { withStyles } from '@mui/styles';
import styles from './styles';
const listTask = [
{
id: 1,
title: 'Read book',
description: 'Read material ui book',
status: 0,
},
{
id: 2,
title: 'Play football',
description: 'With my friend',
status: 2,
},
{
id: 3,
title: 'Play game',
description: 'Alone 😊',
status: 1,
},
];
class TaskBoard extends Component {
renderBoard(states) {
let xhtml = null;
xhtml = (states.map((state,index)=> {
var taskFiltered = listTask.filter(
task => task.status === state.value
);
return (
<ProductItem key={index} state={taskFiltered[0]} index={index} status={state}></ProductItem>
);
}));
return xhtml;
}
render() {
var {states} = this.props;
return (
<ProductList>
{this.renderBoard(states)}
</ProductList>
);
}
}
var mapStateToProps = (state) => {
return {
states: state.states
}
}
export default connect(mapStateToProps, null)(withStyles(styles)(TaskBoard));
src\containers\TaskBoard\styles.js
// Some code
src\components\ProductItem\index.js
import React, { Component } from 'react';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
import Grid from '@mui/material/Grid2';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import Fab from '@mui/material/Fab';
import { withStyles } from '@mui/styles';
import styles from './styles';
class ProductItem extends Component {
render() {
var { classes, state, status } = this.props;
return (
<Grid md={4}>
<Card>
<Box my={2} textAlign="center">
<Typography component="h2">
{status.state}
</Typography>
</Box>
<CardContent>
<Grid container>
<Grid md={6}>
<Typography component="h3">
{state.title}
</Typography>
</Grid>
<Grid md={6}>
<Typography component="h4">
{status.state}
</Typography>
</Grid>
<Grid md={12}>
<Typography component="p">
{state.description}
</Typography>
</Grid>
</Grid>
<CardActions className={classes.CardActions}>
<Fab color="primary" aria-label="add">
<EditIcon></EditIcon>
</Fab>
<Fab color="secondary" aria-label="add">
<DeleteIcon></DeleteIcon>
</Fab>
</CardActions>
</CardContent>
</Card>
</Grid>
);
}
}
export default withStyles(styles)(ProductItem);
src\components\ProductItem\styles.js
var styles = (theme) => ({
CardActions: {
justifyContent: "flex-end",
borderRadius: theme.borderradius
}
});
export default styles;
src\components\ProductList\index.js
import React, { Component } from 'react';
import Grid from '@mui/material/Grid2';
class ProductList extends Component {
render() {
return (
<Grid container spacing={2}>
{this.props.children}
</Grid>
);
}
}
export default ProductList;
src\components\TaskForm\index.js
// Some code
src\components\TaskForm\styles.js
// Some code
src\components\TaskItem\index.js
import React, { Component } from 'react';
import { withStyles } from '@mui/styles';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import Typography from '@mui/material/Typography';
import Grid from '@mui/material/Grid2';
import Fab from '@mui/material/Fab';
import Edit from '@mui/icons-material/Edit';
import styles from './styles';
class TaskItem extends Component {
render() {
const { classes, task, status } = this.props;
const { id, title } = task;
return (
<Card key={id} className={classes.card}>
<CardContent>
<Grid container justify="space-between">
<Grid md={8}>
<Typography component="h2">{title}</Typography>
</Grid>
<Grid md={4}>
{status.label}
</Grid>
</Grid>
<p>{task.description}</p>
</CardContent>
<CardActions className={classes.cardActions}>
<Fab
color="primary"
aria-label="Edit"
className={classes.fab}
size="small"
>
<Edit fontSize="small">edit_icon</Edit>
</Fab>
<Fab
color="primary"
aria-label="Delete"
className={classes.fab}
size="small"
>
<Delete fontSize="small">delete_icon</Delete>
</Fab>
</CardActions>
</Card>
);
}
}
export default withStyles(styles)(TaskItem);
src\components\TaskItem\styles.js
// Some code
src\components\TaskList\index.js
import React, { Component } from 'react';
import { withStyles } from '@mui/styles';
import Grid from '@mui/material/Grid2';
import Box from '@mui/material/Box';
import styles from './styles';
import TaskItem from '../TaskItem';
class TaskList extends Component {
render() {
const { classes, tasks, status } = this.props;
return (
<Grid md={4} xs={12} key={status.value}>
<Box mt={2} mb={2}>
<div className={classes.status}>{status.label}</div>
</Box>
<div className={classes.wrapperListTask}>
{tasks.map(task => {
return <TaskItem task={task} status={status} key={task.id} />;
})}
</div>
</Grid>
);
}
}
export default withStyles(styles)(TaskList);
src\components\TaskList\styles.js
// Some code
src\index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './containers/App';
import myReducer from './reducers/index';
import { thunk } from 'redux-thunk';
import { Provider } from 'react-redux';
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux';
import { ThemeProvider } from '@mui/material/styles';
import theme from './commons/Theme';
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
var store = createStore(myReducer, composeEnhancer(applyMiddleware(thunk)));
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<Provider store={store}><App /></Provider>
</ThemeProvider>
</React.StrictMode>
);
src\commons\Theme\index.js
import { createTheme } from '@mui/material/styles';
var theme = createTheme({
palette: {
secondary: {
main: '#123456'
}
},
borderradius: 40
});
export default theme;
src\components\ProductItem\styles.js
var styles = (theme) => ({
CardActions: {
justifyContent: "flex-start",
borderRadius: theme.borderradius,
backgroundColor: theme.palette.secondary.main,
}
});
export default styles;
Tên cũ dùng state dễ bị nhầm xem ảnh để biết thêm
src\containers\TaskBoard\index.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import ProductList from './../../components/ProductList';
import ProductItem from './../../components/ProductItem';
import { withStyles } from '@mui/styles';
import styles from './styles';
const listTask = [
{
id: 1,
title: 'Read book',
description: 'Read material ui book',
status: 0,
},
{
id: 2,
title: 'Play football',
description: 'With my friend',
status: 2,
},
{
id: 3,
title: 'Play game',
description: 'Alone 😊',
status: 1,
}
];
class TaskBoard extends Component {
renderBoard(states) {
let xhtml = null;
xhtml = (states.map((state, index) => {
var taskFiltered = listTask.filter(
task => task.status === state.value
);
return (
<ProductItem key={index} task={taskFiltered[0]} index={index} status={state}></ProductItem>
);
}));
return xhtml;
}
render() {
var { states } = this.props;
return (
<ProductList>
{this.renderBoard(states)}
</ProductList>
);
}
}
var mapStateToProps = (state) => {
return {
states: state.states
}
}
export default connect(mapStateToProps, null)(withStyles(styles)(TaskBoard));
src\components\ProductItem\index.js
import React, { Component } from 'react';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
import Grid from '@mui/material/Grid2';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import Fab from '@mui/material/Fab';
import { withStyles } from '@mui/styles';
import styles from './styles';
class ProductItem extends Component {
render() {
var { classes, task, status } = this.props;
return (
<Grid md={4}>
<Card>
<Box my={2} textAlign="center">
<Typography component="h2">
{status.state}
</Typography>
</Box>
<CardContent>
<Grid container>
<Grid md={6}>
<Typography component="h3">
{task.title}
</Typography>
</Grid>
<Grid md={6}>
<Typography component="h4">
{status.state}
</Typography>
</Grid>
<Grid md={12}>
<Typography component="p">
{task.description}
</Typography>
</Grid>
</Grid>
<CardActions className={classes.CardActions}>
<Fab color="primary" aria-label="add">
<EditIcon></EditIcon>
</Fab>
<Fab color="secondary" aria-label="add">
<DeleteIcon></DeleteIcon>
</Fab>
</CardActions>
</CardContent>
</Card>
</Grid>
);
}
}
export default withStyles(styles)(ProductItem);