无法从mapstatetoprops方法获取状态

时间:2019-10-18 12:13:41

标签: javascript reactjs redux react-redux

我有如下所示的代码。我的问题是关于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,
})

1 个答案:

答案 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,
}),