【问题标题】:How to filter products by size in react redux如何在 React Redux 中按大小过滤产品
【发布时间】:2020-08-25 21:24:49
【问题描述】:

我是 react 和 redux 的新手。我一直在尝试使用 redux 过滤产品的一些数据,但我对自己的做法不满意。问题是:当页面重新加载时,产品不会呈现,只有当我点击其中一个选项时才会呈现。有没有一种好的和干净的方法呢?我真的很感激任何帮助。谢谢你:)

  1. 这是产品数据
import faker from 'faker';

const PRODUCTS_DATA = [
    {
        id: 1,
        categorie: 'men',
        size: 'S',
        name: faker.commerce.productName(),
        imgUrl: 'http://images.asos-media.com/products/asos-design-oversized-longline-shirt-with-placement-floral-print-in-white/20232840-1-white?$n_480w$&wid=476&fit=constrain',
        price: faker.commerce.price()
    },
    {
        id: 2,
        categorie: 'men',
        size: 'M',
        name: faker.commerce.productName(),
        imgUrl: 'http://images.asos-media.com/products/calvin-klein-jeans-asos-exclusive-sweat-shorts-with-white-noise-back-pocket-logo-in-black/20558434-1-ckblack?$n_480w$&wid=476&fit=constrain',
        price: faker.commerce.price()
    },
    {
        id: 33,
        categorie: 'men',
        size: 'L',
        name: faker.commerce.productName(),
        imgUrl: 'http://images.asos-media.com/products/asos-design-skinny-western-denim-jacket-in-black/12887167-1-black?$n_480w$&wid=476&fit=constrain',
        price: faker.commerce.price()
    }
,
    {
        id: 34,
        categorie: 'men',
        size: 'XL',
        name: faker.commerce.productName(),
        imgUrl: 'http://images.asos-media.com/products/asos-design-skinny-western-denim-jacket-in-black/12887167-1-black?$n_480w$&wid=476&fit=constrain',
        price: faker.commerce.price()
    }
   
]

export default PRODUCTS_DATA;
  1. 这里是 action.js。所以在这里我过滤产品尺寸,如果用户在选项值中选择的尺寸与产品尺寸相同。如果是,它将返回具有所选尺寸的产品列表,否则将返回所有产品。
import ProductsActionTypes from './products.types';


export const filterProductsBySize = (products, size) => ({
    type: ProductsActionTypes.FILTER_PRODUCTS_BY_SIZE,
    payload: {
        size: size,
        items: size === "" ? products : products.filter(product => product.size === size)
    }
});
  1. 这里是 reducer.js

import PRODUCTS_DATA from '../../components/Product-data';
import ProductsActionTypes from './products.types';

const INITIAL_STATE = {
    items: PRODUCTS_DATA,
    filteredItems: []
}

const productsReducer = (state = INITIAL_STATE, action) => {
    switch (action.type) {

        case ProductsActionTypes.FILTER_PRODUCTS_BY_SIZE:
            return {
                ...state,
                filteredItems: action.payload.items
            }

        default:
            return state;

    }
}
export default productsReducer;
  1. 过滤器组件
import React from 'react';

import { connect } from 'react-redux';

import {filterProductsBySize} from '../../redux/products/products.actions';

const ProductFilters = (props) => {
    return (
        <div>
            <div className="jumbotron">
                <div>
                    <label>
                        FILTER BY SIZE
                        <select
                            value={props.size}
                            onChange={(e) => {
                                props.dispatch(filterProductsBySize(props.products, e.target.value))
                            }}
                        >
                            <option value="">ALL</option>
                            <option value="S">S</option>
                            <option value="M">M</option>
                            <option value="L">L</option>
                            <option value="XL">XL</option>
                        </select>
                    </label>
                </div>
            </div>
        </div>
    )
}

const mapStateToProps = (state) => ({
    products: state.products.items
})

export default connect(mapStateToProps)(ProductFilters);
  1. 这是重新渲染产品的组件
import React from 'react';

import { connect } from 'react-redux';

import Card from '../../Card';
import ProductFilters from '../../product-filters/ProductFilters';

const MensPage = (props) => {
    return (
        <div className="container">
            <h1>Men Page</h1>
            <ProductFilters />
            <div className="row">
                {
                    props.products.filter(product => product.categorie === 'men')
                        .map(product => <Card key={product.id} product={product} />)
                }
            </div>
        </div>
    )
};

const mapStateToProps = (state) => ({
    products: state.products.filteredItems
})

export default connect(mapStateToProps)(MensPage);

【问题讨论】:

    标签: reactjs react-redux filtering


    【解决方案1】:

    在 Product 渲染组件中,您选择的是 products.filteredItems,它最初是一个空数组。

    正确的方法-

    在行动 -

    export const filterProductsBySize = (products, size) => ({
        type: ProductsActionTypes.FILTER_PRODUCTS_BY_SIZE,
        payload: {
            size: size,
      }
    }
    

    在减速器中

    const INITIAL_STATE = {
        items: PRODUCTS_DATA, 
    }
    
    case ProductsActionTypes.FILTER_PRODUCTS_BY_SIZE:
           return state.items.filter(item => item.size === action.payload.size)
    

    【讨论】:

    • 感谢您的回复,但我在 MensPage 组件中出现以下错误:TypeError: Cannot read property 'filter' of undefined。 PS:我将filteredItems更改为mapState中的项目。
    • 在遵循我的代码之后?您是否更改了 mapStateToProps:state.products.items?
    • 是的,先生,我把它改成了物品。
    • 在男士页面组件中放置一个 consol.log 并检查您是否处于状态。
    • 我正在从 console.log(props.products) 获取所有项目,但是当我选择任何选项(S、M 或 L)时,它会给我这个错误。
    【解决方案2】:

    您永远不会将大小保存到您的状态,也永远不会从状态中选择大小,因此在代码 &lt;select value={props.size} 中,props.size 始终未定义。您可以将大小保存在 redux 状态并使用选择器来过滤产品。

    你不应该在 redux 状态下保存副本或计算结果,因为过滤的产品是产品的计算,你应该使用选择器。

    这是一个工作示例:

    const {
      Provider,
      useSelector,
      connect,
    } = ReactRedux;
    const { createStore, applyMiddleware, compose } = Redux;
    const { createSelector } = Reselect;
    
    const initialState = {
      items: [
        { size: 'S', id: 1 },
        { size: 'M', id: 2 },
        { size: 'L', id: 3 },
        { size: 'XL', id: 4 },
        { size: 'XL', id: 5 },
      ],
      size: '',
    };
    //action types
    const CHANGE_SIZE = 'CHANGE_SIZE';
    //action creators
    const changeSize = (size) => ({
      type: CHANGE_SIZE,
      payload: size,
    });
    const reducer = (state, { type, payload }) => {
      if (type === CHANGE_SIZE) {
        return { ...state, size: payload };
      }
      return state;
    };
    //selectors
    const selectItems = (state) => state.items;
    const selectSize = (state) => state.size;
    const createSelectFilteredItems = (size) =>
      createSelector(
        [selectItems, selectSize],
        (items, filterSize) =>
          size
            ? items.filter(({ size }) => size === filterSize)
            : items
      );
    //creating store with redux dev tools
    const composeEnhancers =
      window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    const store = createStore(
      reducer,
      initialState,
      composeEnhancers(
        applyMiddleware(() => (next) => (action) =>
          next(action)
        )
      )
    );
    const ProductFilters = ({ size, changeSize }) => {
      return (
        <div>
          <label>
            FILTER BY SIZE
            <select
              value={size}
              onChange={(e) => {
                changeSize(e.target.value);
              }}
            >
              <option value="">ALL</option>
              <option value="S">S</option>
              <option value="M">M</option>
              <option value="L">L</option>
              <option value="XL">XL</option>
            </select>
          </label>
        </div>
      );
    };
    
    const mapStateToProps = (state) => ({
      size: selectSize(state),
    });
    
    const ProductFiltersContainer = connect(mapStateToProps, {
      changeSize,
    })(ProductFilters);
    const ProductList = () => {
      const size = useSelector(selectSize);
      const selectFilteredItems = React.useMemo(
        () => createSelectFilteredItems(size),
        [size]
      );
      const filteredProducts = useSelector(selectFilteredItems);
      return (
        <ul>
          {filteredProducts.map((p) => (
            <li key={p.id}>
              <pre>{JSON.stringify(p, undefined, 2)}</pre>
            </li>
          ))}
        </ul>
      );
    };
    const App = () => (
      <div>
        <ProductFiltersContainer />
        <ProductList />
      </div>
    );
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>
    <div id="root"></div>

    更多关于如何使用选择器的文档可以在here找到

    【讨论】:

    • 谢谢先生的回复。但是我在代码的某些部分迷失了自己,是否可以用我给你的代码举个例子?在此先感谢:)
    • @mr_vmoore 使用 changeSize 创建的操作将设置 state.size 并且选择器 createSelectFilteredItems 创建一个选择器以过滤state.items。可以找到关于如何使用选择器的详细文档here
    • 谢谢,你说得对,我必须使用 a 选择器。经过一些关于选择器的谷歌搜索和教程后,我终于找到了一种简单且不太复杂的方法,到目前为止它运行良好。非常感谢你给我指路:)
    猜你喜欢
    • 1970-01-01
    • 2021-01-15
    • 2021-11-14
    • 2021-01-10
    • 1970-01-01
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多