istcomponent
应该在我添加或更新产品后重新呈现,但不是,我尝试使用immer,但结果是相同的这是我的减速器
import {
PRODUCT_REMOVED,
PRODUCT_ADDED,
PRODUCT_UPDATED,
ADD_ERROR,
PRODUCT_LIST,
LIST_ERROR
} from "../actions/types";
// define default redux state
const DEFAULT_STATE = {
products: [],
errorMSG: ""
};
/**
* return new state with new product subtree based on action type
* @param {Object} state redux old state
* @param {Object} action action performed
* @return {Object} redux new state
*/
export default (state = DEFAULT_STATE, action) => {
let new_products;
let i;
let j;
// return new state with new product subtree based on action type
switch (action.type) {
case PRODUCT_ADDED:
new_products = state.products;
new_products.unshift(action.payload);
return {
...state,
products: new_products,
errorMSG: ""
};
case PRODUCT_UPDATED:
new_products = state.products;
for (i = 0; i < new_products.length; i++)
if (new_products[i]._id === action.payload._id) {
new_products[i] = action.payload;
break;
}
return {
...state,
products: new_products,
errorMSG: ""
};
case PRODUCT_REMOVED:
new_products = state.products;
for (i = 0; i < new_products.length; i++)
if (new_products[i]._id === action.payload._id) {
new_products.splice(i, 1);
break;
}
return {
...state,
products: new_products,
errorMSG: ""
};
case ADD_ERROR:
return {
...state,
errorMSG: action.payload
};
case PRODUCT_LIST:
return {
...state,
products: action.payload,
errorMSG: ""
};
case LIST_ERROR:
return {
...state,
errorMSG: action.payload
};
default:
return state;
}
};
和我的减根剂
import { combineReducers } from "redux";
import authReducer from "./auth";
import subjectReducer from "./subject";
import gradeReducer from "./grade"
import studentReducer from "./student"
import productReducer from "./product"
import teacherReducer from "./teacher"
//create root reducer that will contail other reducers
const rootReducer = combineReducers({
auth: authReducer,
subject: subjectReducer,
grade: gradeReducer,
student: studentReducer,
teacher:teacherReducer,
product:productReducer,
});
export default rootReducer;
和我的组件
import React from 'react';
import { connect } from "react-redux";
import { compose } from "redux";
import { withStyles } from "@material-ui/core/styles";
import PropTypes from "prop-types";
import { validate_name,validate_num } from "../../utils/validators";
import CardActionArea from '@material-ui/core/CardActionArea';
import CardActions from '@material-ui/core/CardActions';
import AddProduct from "./AddProduct";
import Spinner from "../../Spinner";
import TextField from "@material-ui/core/TextField";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle";
import Button from '@material-ui/core/Button';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
//import AddSubject from './AddSubject'
import * as actions from "../../actions/ProductActions";
import Product from './Product'
const styles = theme => ({
root: {
flexGrow: 1,
},
paper: {
padding: theme.spacing(2),
textAlign: 'center',
color: theme.palette.text.secondary,
}});
class ListProduct extends React.Component {
constructor(props) {
super(props);
this.state = {
is_loading: false,
open:false,
err: '',
openDelete:false
}
}
/**
* handle edit after add button is clicked
* @param {Object} e click event
*/
async handleSubmit(e) {
e.preventDefault();
let name_isValid = await validate_name('Name',this.state.name);
let description_isValid = await validate_name("Description ", this.state.description);
let price_isValid = await validate_num("Price", this.state.price);
let stock_isValid = await validate_num("Stock", this.state.stock);
if ((name_isValid.status && description_isValid.status&& price_isValid.status&& stock_isValid.status) === true) {
// call product actions creator
await this.props.editProduct({
product_id:this.state.id,
name: this.state.name,
description: this.state.description,
price: this.state.price,
stock: this.state.stock,
});
let { errorMSG } = this.props.product;
if (errorMSG) {
this.setState({ err: errorMSG });
} else {
this.setState({ err: "",open:false });
}
} else {
if (name_isValid.status === false)
this.setState({ err: name_isValid['text'] });
else if (description_isValid.status === false)
this.setState({ err: description_isValid['text'] });
else if (price_isValid.status === false)
this.setState({ err: price_isValid['text'] });
else if (stock_isValid.status === false)
this.setState({ err: stock_isValid['text'] });
}
}
/**
* handle change in inputs
* @param {String} name input label
*/
handleChange = name => event => {
this.setState({
[name]: event.target.value
});
};
/**
* close/open dialog box
*/
toggleDialog = () => {
this.setState({open:!this.state.open });
};
/**
* handle edit after enter key is pressed
* @param {Object} event key press event
*/
handleSubmitWithEnterKey = async event => {
if (event.key === "Enter") {
event.preventDefault();
let name_isValid = await validate_name('Name',this.state.name);
let description_isValid = await validate_name("Description ", this.state.description);
let price_isValid = await validate_num("Price", this.state.price);
let stock_isValid = await validate_num("Stock", this.state.stock);
if ((name_isValid.status && description_isValid.status&& price_isValid.status&& stock_isValid.status) === true) {
// call product actions creator
await this.props.editProduct({
product_id:this.state.id,
name: this.state.name,
description: this.state.description,
price: this.state.price,
stock: this.state.stock,
});
let { errorMSG ,products} = this.props.product;
if (errorMSG) {
this.setState({ err: errorMSG });
} else {
this.setState({ err: "",open:false });
}
} else {
if (name_isValid.status === false)
this.setState({ err: name_isValid['text'] });
else if (description_isValid.status === false)
this.setState({ err: description_isValid['text'] });
else if (price_isValid.status === false)
this.setState({ err: price_isValid['text'] });
else if (stock_isValid.status === false)
this.setState({ err: stock_isValid['text'] });
}
}
};
/**
* toggle edit dialog
* @param {String} id product id
* @param {String} name product name
* @param {Number} price product price
* @param {Number} stock product stock
* @param {String} description product description
*/
toggleEditDialog = ( id ,name ,price ,stock , description) => e => {
this.setState({
open: !this.state.open,
id: id,
name: name,
price: price,
stock: stock,
description: description,
err: ""
});
};
/**
* handle confirm delete
* @param {Object} e button click even
*/
async handleDelete(e) {
e.preventDefault();
// call product actions creator
await this.props.removeProduct({
product_id: this.state.id
});
let { errorMSG,products } = this.props.product;
if (errorMSG) {
this.setState({ err: errorMSG });
} else this.setState({ openDelete: false, err: "",products:products });
}
/**
* toggle delete dialog
* @param {String} id product id
*/
toggleDeleteDialog = id => e => {
this.setState({ openDelete: !this.state.openDelete, id: id, err: "" });
};
/**
* buy a product
* @param {String} id product id
*/
buy = id => async e => {
// call product actions creator
await this.props.buyProduct({
product_id:this.state.id});
let { errorMSG ,products} = this.props.product;
if (errorMSG) {
this.setState({ err: errorMSG });
} else {
this.setState({ err: "" });
}
};
/**
* handle change in inputs
* @param {String} name input label
*/
handleChange = name => event => {
this.setState({
[name]: event.target.value });
};
async componentDidMount() {
await this.props.listProduct();
let { errorMSG ,products} = this.props.product;
if (errorMSG) {
this.setState({ err: errorMSG});
} else this.setState({ err: "" ,products:products });
}
render() {
// if ((typeof this.state.data === 'undefined') || (this.state.is_loading)) {
// return (
// <div data-test="ListProductComponent">
// <Spinner/>
// </div>)
// }
const{classes}=this.props
let { products } = this.props.product;
products=(typeof products==='undefined')?[]:products
return (
<div className={classes.root}>
<AddProduct />
<br/>
<Grid container spacing={3}>
{products
.map(product => (
<Grid item xs={12} sm={4} lg={3}>
<Product currentProduct={product} />
<CardActions>
<Button onClick={this.buy(product._id)} size="small" color="primary">
Buy
</Button>
<Button onClick={this.toggleEditDialog(product._id,product.name,product.price,product.stock,product.description)}size="small" color="primary">
Edit
</Button>
<Button onClick={this.toggleDeleteDialog(product._id)} size="small" color="secondary">
Delete
</Button>
{product.price +' KSH'}
</CardActions>
</Grid>))}
</Grid>
<Dialog
data-test="EditDialog"
open={this.state.open}
onClose={this.toggleEditDialog("", "", "", "", "", "", "", "","", "", "", "", "", "", "", "")}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">Edit Product</DialogTitle>
<DialogContent>
<TextField
id="name"
label="Name"
onChange={this.handleChange("name")}
margin="normal"
value={this.state.name}
autoFocus={true}
onKeyDown={this.handleSubmitWithEnterKey.bind(this)}
/>
<br />
<TextField
id="description"
label="Description"
onChange={this.handleChange("description")}
margin="normal"
value={this.state.description}
onKeyDown={this.handleSubmitWithEnterKey.bind(this)}
/>
<br />
<TextField
id="price"
label="Price"
onChange={this.handleChange("price")}
margin="normal"
value={this.state.price}
onKeyDown={this.handleSubmitWithEnterKey.bind(this)}
/>
<TextField
id="stock"
label="Stock"
onChange={this.handleChange("stock")}
margin="normal"
value={this.state.stock}
onKeyDown={this.handleSubmitWithEnterKey.bind(this)}
/>
<h1>{this.state.err}</h1>
</DialogContent>
<DialogActions>
<Button
onClick={this.toggleEditDialog( false, false, false, false, false)}
color="primary"
>
Cancel
</Button>
<Button onClick={this.handleSubmit.bind(this)} color="primary">
Edit
</Button>
</DialogActions>
</Dialog>
<Dialog
data-test="DeleteDialog"
open={this.state.openDelete}
onClose={this.toggleDeleteDialog("")}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">Delete Product</DialogTitle>
<DialogActions>
<Button onClick={this.toggleDeleteDialog("")} color="primary">
Cancel
</Button>
<Button color="secondary" onClick={this.handleDelete.bind(this)}>
Confirm Delete
</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
ListProduct.propTypes = {
classes: PropTypes.object.isRequired,
isAuthenticated: PropTypes.bool.isRequired
};
/**
* map redux this.state to component props
* @param {Object } this.state redux this.state
* @return {object} component props
*/
function mapStatetoProps(state) {
return {
product: state.product,
rank: state.auth.rank,
isAuthenticated: state.auth.isAuthenticated
};
}
export default compose(
connect(
mapStatetoProps,
actions
),
withStyles(styles)
)(ListProduct);
任何人都可以告诉我我做错了什么
尝试类似的东西:
case PRODUCT_ADDED:
return {
...state,
products: [...state.products, action.payload],
errorMSG: ""
};
state.products
分配给new_products
。使用价差运算符case PRODUCT_ADDED:
new_products = ...(state.products);
new_products.unshift(action.payload);
return {
...state,
products: new_products,
errorMSG: ""
};