我有如下所示的代码。我的问题是关于react和redux的用法。我无法通过mapstate方法获取状态“类别”。当我尝试通过Product.js中的this.props.categories控制台类别时,它显示为未定义。我已经附加了带减速器的product.js文件,另一个是我的主要组件Product.js。
这是我的Product.js代码:
import React from 'react'
import {connect} from 'react-redux'
import withStyles from 'isomorphic-style-loader/lib/withStyles'
import s from './Product.css'
import { Select, Button, Table, Tooltip, InputNumber, Card} from 'antd'
import ProductForm from './ProductForm'
import {withCurrency} from '../../components'
import debounce from 'lodash/debounce'
import {
changeSelectedProduct,
getProducts,
addProduct,
changeMarkupFactor,
updateProduct,
currencyClicked,
editRetailPrice,
retailPriceUpdated,
handleChange
} from '../../reducers/product'
const children = ["Home","Health","Beauty","Pregnancy","Kits","Animals","Travel","Cooking"]
class Product extends React.Component {
constructor(props) {
super(props)
this.getProducts = debounce(props.getProducts, 800);
}
componentWillMount() {
// TODO get initial list of popular products
this.props.getProducts()
}
handleChange = (values) => {
console.log(values)
let Values = children.filter( (eachone,index) =>{
var includes = values.includes(`${index}`)
if(includes){
return true
}
}
)
this.props.handleChange(Values)
}
render() {
const {
products,
loading,
selectedProduct,
onSubmit,
formRef,
markupFactor,
changeMarkupFactor,
changeSelectedProduct,
currencies,
totalPrice,
currency,
clearForms,
addRetailPrice,
currencyClicked,
editRetailPrice,
retailPriceUpdated,
categories
} = this.props;
console.log('entire state', this.props.state)
const columns = [
{
key: 'name',
dataIndex: 'name',
width: 130,
className: s.nameColumn,
},
{
key: 'price',
dataIndex: 'price',
className: s.priceColumn,
render: (price, row) => {
if (row.id === 'retail-price' && addRetailPrice) {
return (
<InputNumber
value={totalPrice.retail_price[currency.key]}
onChange={(value) => editRetailPrice(value, currency.key)}
defaultValue={price[currency.key]}
onBlur={() => retailPriceUpdated(currency.key)}
/>
);
}
return (
<Tooltip
placement='topRight'
title={
currencies.map(item =>
currency.key !== item.key ? (
<div key={item.key}>
{withCurrency(item, price[item.key])}
</div>
) : null
)
}
>
<div onClick={() => currencyClicked(row)}>
{withCurrency(currency, price[currency.key])}
</div>
</Tooltip>
)
}
}
]
let data = []
if (totalPrice) {
data = [
{id:'receipe', name: 'Recipe', price: totalPrice.recipe.total_price},
{id:'container', name: 'Container', price: totalPrice.container.total_price},
{id:'wholesale-price', name: 'Wholesale Price', price: totalPrice.wholesale_price},
{id:'retail-price', name: 'Retail Price', price: totalPrice.retail_price},
]
}
return (
<Card
className={s.container}
title={'Product'}
bodyStyle={{minHeight: 409}}
extra = {<a onClick={clearForms}>{'Clear'}</a>}
>
{/* <div className={s.container}> */}
{/* <div className={s.headerWrapper}>
<h3 className={s.header}>{'Product'}</h3> */}
{/*use clear here below */}
{/* <a onClick={clearForms}>{'Clear'}</a>
</div> */}
<div className={s.containerSearchWrapper}>
<Select
className = {s.search}
showSearch
allowClear
value={selectedProduct ? `${selectedProduct.id}` : undefined}
className={s.productSearch}
placeholder='Search'
notFoundContent={loading.products ? <Spin size='small'/> : null}
filterOption={false}
onSearch={(search) => this.getProducts({search})}
onChange={(id) => {
const newProduct = products.find(item => item.id === +id)
changeSelectedProduct(newProduct)
}}
>
{products.map(item =>
<Select.Option key={item.id}>{item.name}</Select.Option>
)}
</Select>
</div>
<ProductForm
markupFactor={markupFactor}
changeMarkupFactor={changeMarkupFactor}
ref={formRef}
children={children}
handleChange={this.handleChange}
/>
<Table
className={s.table}
columns={columns}
dataSource={data}
loading={loading.totalPrice}
size='small'
rowKey={(record, i) => i}
pagination={false}
showHeader={false}
locale={{emptyText: 'Total price'}}
/>
<div className={s.actions}>
<Button
onClick={() => onSubmit('update')}
loading={loading.updatingProduct}
disabled={!selectedProduct}
>
{'Save'}
</Button>
<Button
onClick={() => onSubmit('add')}
loading={loading.addingProduct}
>
{'Create new'}
</Button>
</div>
{/* </div> */}
</Card>
)
}
}
const mapState = state => ({
...state.product,
currency: state.global.currency,
currencies: state.global.currencies,
categories: state.global.categories,
})
const mapDispatch = {
getProducts,
addProduct,
updateProduct,
changeMarkupFactor,
changeSelectedProduct,
currencyClicked,
editRetailPrice,
retailPriceUpdated,
handleChange,
}
export default connect(mapState, mapDispatch)(withStyles(s)(Product))
这是我的product.js代码
import createReducer, {RESET_STORE} from '../createReducer'
import {getToken} from './user'
import qs from 'query-string'
import _ from 'lodash';
import {message} from 'antd'
import messages from '../messages'
import {getFieldValue} from '../utils'
// ------------------------------------
// Constants
// ------------------------------------
export const GET_PRODUCT_REQUEST = 'Product.GET_PRODUCT_REQUEST'
export const GET_PRODUCT_SUCCESS = 'Product.GET_PRODUCT_SUCCESS'
export const GET_PRODUCT_FAILURE = 'Product.GET_PRODUCT_FAILURE'
export const ADD_PRODUCT_REQUEST = 'Product.ADD_PRODUCT_REQUEST'
export const ADD_PRODUCT_SUCCESS = 'Product.ADD_PRODUCT_SUCCESS'
export const ADD_PRODUCT_FAILURE = 'Product.ADD_PRODUCT_FAILURE'
export const UPDATE_PRODUCT_REQUEST = 'Product.UPDATE_PRODUCT_REQUEST'
export const UPDATE_PRODUCT_SUCCESS = 'Product.UPDATE_PRODUCT_SUCCESS'
export const UPDATE_PRODUCT_FAILURE = 'Product.UPDATE_PRODUCT_FAILURE'
export const GET_TOTAL_PRICE_REQUEST = 'Product.GET_TOTAL_PRICE_REQUEST'
export const GET_TOTAL_PRICE_SUCCESS = 'Product.GET_TOTAL_PRICE_SUCCESS'
export const GET_TOTAL_PRICE_FAILURE = 'Product.GET_TOTAL_PRICE_FAILURE'
export const CHANGE_SELECTED_PRODUCT = 'Product.CHANGE_SELECTED_PRODUCT'
export const CHANGE_MARKUP_FACTOR = 'Product.CHANGE_MARKUP_FACTOR'
export const CHANGE_RETAIL_PRICE = 'Product.CHANGE_RETAIL_PRICE';
export const EDITING_RETAIL_PRICE = 'Product.EDITING_RETAIL_PRICE';
export const RETAIL_PRICE_UPDATED = 'Product.RETAIL_PRICE_UPDATED';
export const CLEAR = 'Product.CLEAR'
export const ADD_CATEGORIES = 'Product.ADD_CATEGORIES'
// ------------------------------------
// Actions
// ------------------------------------
// ------------------------------------
// Actions
// ------------------------------------
export const changeSelectedProduct = (selectedProduct) => (dispatch, getState) => {
dispatch({type: CHANGE_SELECTED_PRODUCT, selectedProduct})
// dispatch(changeSelectedComponents(selectedProduct ? selectedProduct.components : []))
}
export const currencyClicked = (row) => (dispatch) => {
if (!row.id === 'retail-price') return;
dispatch({type: CHANGE_RETAIL_PRICE})
}
export const editRetailPrice = (value, currencyKey) => (dispatch, getState) => {
const { totalPrice } = getState().product;
totalPrice.retail_price[currencyKey] = value;
dispatch({type: EDITING_RETAIL_PRICE, totalPrice});
}
export const retailPriceUpdated = (currencyKey) => (dispatch, getState) => {
const { totalPrice } = getState().product;
const markupFactor = parseFloat(totalPrice.retail_price[currencyKey] / totalPrice.wholesale_price[currencyKey]).toFixed(2);
dispatch({type: RETAIL_PRICE_UPDATED, totalPrice});
dispatch({type: CHANGE_MARKUP_FACTOR, markupFactor});
}
export const getProducts = (params = {}) => (dispatch, getState, {fetch}) => {
dispatch({type: GET_PRODUCT_REQUEST, params})
const {token} = dispatch(getToken())
const {search, ordering} = getState().product
return fetch(`/pands/products/?${qs.stringify({
search,
ordering,
})}`, {
method: 'GET',
token,
success: (res) => dispatch({type: GET_PRODUCT_SUCCESS, res}),
failure: (err) => dispatch({type: GET_PRODUCT_FAILURE}),
})
}
export const addProduct = (values) => (dispatch, getState, {fetch}) => {
dispatch({type: ADD_PRODUCT_REQUEST})
const {token} = dispatch(getToken())
const {currencies} = getState().global
const {selectedRecipe} = getState().recipe
const {selectedContainer} = getState().container
return fetch(`/pands/products/`, {
method: 'POST',
body: {
...values,
recipe: selectedRecipe.id,
container: selectedContainer.id,
currencies: currencies.map(item => item.key),
},
token,
success: (selectedProduct) => {
dispatch({type: ADD_PRODUCT_SUCCESS, selectedProduct})
message.success(messages.addProductSuccess)
},
failure: (err) => {
dispatch({type: ADD_PRODUCT_FAILURE})
message.error(messages.addProductError)
},
})
}
export const updateProduct = (values) => (dispatch, getState, {fetch}) => {
dispatch({type: UPDATE_PRODUCT_REQUEST})
const {token} = dispatch(getToken())
const {currencies} = getState().global
const {selectedProduct} = getState().product
const {selectedRecipe} = getState().recipe
const {selectedContainer} = getState().container
return fetch(`/pands/products/${selectedProduct.id}/`, {
method: 'PATCH',
body: {
...values,
recipe: selectedRecipe.id,
container: selectedContainer.id,
currencies: currencies.map(item => item.key),
},
token,
success: (selectedProduct) => {
dispatch({type: UPDATE_PRODUCT_SUCCESS, selectedProduct})
message.success(messages.updateProductSuccess)
},
failure: (err) => {
dispatch({type: UPDATE_PRODUCT_FAILURE})
message.error(messages.updateProductError)
},
})
}
export const getTotalPrice = () => (dispatch, getState, {fetch}) => {
const {token} = dispatch(getToken())
dispatch({type: GET_TOTAL_PRICE_REQUEST})
const {currencies} = getState().global
const {markupFactor} = getState().product
const {fields: {ingredients}} = getState().rawMaterials
const {fields: {components}} = getState().components
return fetch(`/pands/products/calculate/`, {
method: 'POST',
token,
body: {
recipe: {
ingredients: getFieldValue(ingredients),
},
container: {
components: getFieldValue(components),
},
markup_factor: +markupFactor,
currencies: currencies.map(item => item.key),
},
success: (totalPrice) => dispatch({type: GET_TOTAL_PRICE_SUCCESS, totalPrice}),
failure: (error) => dispatch({type: GET_TOTAL_PRICE_FAILURE, error}),
})
}
export const changeMarkupFactor = (markupFactor) => (dispatch, getState) => {
dispatch({type: CHANGE_MARKUP_FACTOR, markupFactor})
dispatch(getTotalPrice())
}
export const clear = () => ({type: CLEAR})
// -----------------------------------
export const handleChange = (values) => (dispatch) => {
dispatch({type: ADD_CATEGORIES,values});
}
// ------------------------------------
// Reducer
// -----------------------------------
const initialState = {
loading: {
products: false,
addingProduct: false,
updatingProduct: false,
totalPrice: false,
},
products: [],
selectedProduct: null,
error: null,
totalPrice: null,
markupFactor: 1.3,
addRetailPrice: false,
categories:[],
}
export default createReducer(initialState, {
[GET_PRODUCT_REQUEST]: (state, {params}) => ({
search: _.has(params, 'search') ? params.search : state.search,
ordering: params.sorter ? `${params.sorter.order === 'descend' ? '-' : ''}${params.sorter.field}` : state.ordering,
filters: params.filters || state.filters,
loading: {
...state.loading,
products: false,
},
}),
[GET_PRODUCT_SUCCESS]: (state, {res: {results}}) => ({
products: results,
loading: false,
}),
[GET_PRODUCT_FAILURE]: (state, action) => ({
loading: false,
}),
[ADD_PRODUCT_REQUEST]: (state, action) => ({
loading: {
...state.loading,
addingProduct: true,
},
}),
[ADD_PRODUCT_SUCCESS]: (state, {selectedProduct}) => ({
selectedProduct,
loading: {
...state.loading,
addingProduct: false,
},
}),
[ADD_PRODUCT_FAILURE]: (state, action) => ({
loading: {
...state.loading,
addingProduct: false,
},
}),
[UPDATE_PRODUCT_REQUEST]: (state, action) => ({
loading: {
...state.loading,
updatingProduct: true,
},
}),
[UPDATE_PRODUCT_SUCCESS]: (state, {selectedProduct}) => ({
selectedProduct,
loading: {
...state.loading,
updatingProduct: false,
},
}),
[UPDATE_PRODUCT_FAILURE]: (state, action) => ({
loading: {
...state.loading,
updatingProduct: false,
},
}),
[GET_TOTAL_PRICE_REQUEST]: (state, action) => ({
totalPrice: null,
loading: {
...state.loading,
totalPrice: true,
},
}),
[GET_TOTAL_PRICE_SUCCESS]: (state, {totalPrice}) => ({
totalPrice,
loading: {
...state.loading,
totalPrice: false,
}
}),
[EDITING_RETAIL_PRICE]: (state, {totalPrice}) => ({
totalPrice
}),
[RETAIL_PRICE_UPDATED]: (state, {totalPrice}) => ({
totalPrice,
addRetailPrice: false
}),
[GET_TOTAL_PRICE_FAILURE]: (state, {error}) => ({
error,
loading: {
...state.loading,
totalPrice: false,
},
}),
[CHANGE_MARKUP_FACTOR]: (state, {markupFactor}) => ({
markupFactor,
}),
[CHANGE_SELECTED_PRODUCT]: (state, {selectedProduct}) => ({
selectedProduct,
}),
[CHANGE_RETAIL_PRICE]: (state) => ({
addRetailPrice: true,
}),
[ADD_CATEGORIES]: (state,{ categories }) => ({
categories
}),
[CLEAR]: (state, action) => RESET_STORE,
})
答案 0 :(得分:0)
在每个化简器中,您必须返回以前的状态和新的状态。
[ACTION]:(state, ...) => ({
...state, <------ previouse state
... < ------- your new data (loading, others)
}),
现在您不返回以前的商店状态,而是仅用新数据覆盖商店
[GET_PRODUCT_REQUEST]: (state, {params}) => ({
...state,
...
}),
[GET_PRODUCT_SUCCESS]: (state, {res: {results}}) => ({
...state,
...
products: results,
loading: false,
}),