D
D
Dmitry2019-04-26 17:53:23
React
Dmitry, 2019-04-26 17:53:23

Is it correct to handle data in a React component's container?

Hello, there is a component

components/pages/ShopPage.jsx

import React, { Component } from 'react';
import ProductSingle from '../products/ProductSingle';
import CatsListFilterShop from '../../containers/CatsListFilterShop';
import ProductsSortShop from '../../containers/ProductsSortShop';
import {Pagination} from "../../helpers/pagination";

import Breadcrumbs from "../../helpers/breadcrumbs";

class ShopPage extends Component {

    componentDidMount() {
        const { setProducts, setAllProducts } = this.props;
        setAllProducts();
    }

    render(){
        const {productsList, categories, isProductsReady, setPagination, currentPage, pager} = this.props;
        
        const matchPath = this.props.match.path;

        return (
            <div className="container woocomm__container">
                <div className="row woocomm__row">
                    <div className="col-xs-12">
                        <div className="woocomm__col">

                            <Breadcrumbs />

                            <div className="products__wrapper">
                                <div className="products__sidebar">
                                    {isProductsReady && <CatsListFilterShop categories={categories} />}
                                </div>

                                <div className="products__content">
                                    <div className="products__contentHeader">
                                        <div className="products__contentHeaderTitle">Весь ассортимент</div>

                                        <ProductsSortShop />
                                    </div>

                                    <div className="products__list">
                                        {isProductsReady && (
                                            productsList ? (
                                                productsList.map( ( productData ) => {
                                                    return (
                                                        <div className={'good__item'} key={productData.id}>
                                                            <ProductSingle {...productData} matchPath={matchPath} />
                                                        </div>
                                                    );
                                                })) : (
                                                <div className="products__list-empty">В этой категории товаров нет</div>
                                            )
                                        )}
                                    </div>

                                    {pager && (
                                        <Pagination
                                            pager={pager}
                                            page={currentPage}
                                            setPagination={setPagination.bind(this)}
                                        />
                                    )}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

export default ShopPage;

and its container in which filtering and other data processing takes place.
containers/pages/ShopPage.js

import {connect} from 'react-redux';
import {setProducts, setAllProducts} from '../../actions/products';
import {setFilter, setPagination} from '../../actions/filter';
import ShopPage from '../../components/pages/ShopPage';
import {getPager} from "../../helpers/pagination";
import {getCategoryProductRelationsByCatSlug} from '../../helpers/getCategoryProductRelations';


function sortBy(products, sortBy){
    switch(sortBy){
        case 'price_asc':
            return products && products.sort((a, b) => (a.regular_price - b.regular_price));
        case 'price_desc':
            return products && products.sort((a, b) => (b.regular_price - a.regular_price))
        default:
            return products && products;
    }
}

function getVisibleProducts(products, filterBy, catsRelation){
    switch(filterBy){
        case "all":
            return products && products;
        default:
            const productIDs = catsRelation[filterBy] ? catsRelation[filterBy] : [];
            return products && products.filter(item => (productIDs.includes(item.id)));
    }
}

function getPaginatedProducts(products, page, perPage){
    const end = page * perPage;
    const begin = end - perPage;

    return products && products.slice(begin, end);
};

function getPages(visibleProducts, perPage){
    return Math.ceil(visibleProducts && visibleProducts.length / perPage);
};



const mapStateToProps = ({products, filter}) => {
    const categoriesRelationship = getCategoryProductRelationsByCatSlug( products.items.categoriesRelationship );

    const visibleProducts = getVisibleProducts(
        products.items.productsList,
        filter.filterShopBy,
        categoriesRelationship
    );

    const sortedProducts = sortBy(
        visibleProducts,
        filter.sortProductShopBy
    );

    /* START: Pagination */
    const perPage = 9;

    const currentPage = filter.page || 1;

    const paginatedProducts = getPaginatedProducts(
        sortedProducts,
        currentPage,
        perPage
    );

    const pager = getPager(
        visibleProducts && visibleProducts.length,
        currentPage,
        perPage
    );
    /* END: Pagination */

    return {
        productsList: paginatedProducts,
        categories: products.items.categories,
        categoriesRelationship: categoriesRelationship,
        isProductsReady: products.isProductsReady,
        filterBy: filter.filterShopBy,
        sortProductShopBy: filter.sortProductShopBy,
        pager: pager,
        currentPage: currentPage,
    }
};

const mapDispatchToProps = dispatch => ({
    setAllProducts: () => dispatch(setAllProducts()),
    setProducts: products => dispatch(setProducts(products)),
    setPagination: page => dispatch(setPagination(page)),
    setFilter,
});

export default connect(mapStateToProps, mapDispatchToProps)(ShopPage);

respectively
actions/products.js

import {
    SET_PRODUCTS,
    SET_PRODUCTS_SUCCEEDED,
    SET_PRODUCTS_FAILED,
} from './types/product-types';

export const setAllProducts = () => {
    return async dispatch => {
        dispatch({type: SET_PRODUCTS});
        axios.get('/api/products').then(({data}) =>{
            dispatch({type: SET_PRODUCTS_SUCCEEDED, payload: data});
        }).catch(err => {
            dispatch({type: SET_PRODUCTS_FAILED, payload: err});
        });
    };
};

and
reducers/products.js

import {
    SET_PRODUCTS,
    SET_PRODUCTS_SUCCEEDED,
    SET_PRODUCTS_FAILED,
} from '../actions/types/product-types';

const INITIAL_STATE = {
    isProductsReady: false,
    isProductsLoading: false,
    productsError: null,
    items: [],
};

export default function (state = INITIAL_STATE,action){
    switch (action.type) {
        case SET_PRODUCTS:
            return {
                ...state,
                isProductsReady: false,
                isProductsLoading: true,
            };
        case SET_PRODUCTS_SUCCEEDED:
            return {
                ...state,
                items: action.payload,
                isProductsReady: true,
                isProductsLoading: false,
            };
        case SET_PRODUCTS_FAILED:
            return {
                ...state,
                isProductsReady: false,
                isProductsLoading: false,
                productsError: action.payload
            };
        default:
            return state;
    }
}

Is it correct to filter in the container or should it be done in the reducer or actions?
Thank you.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
R
Robur, 2019-04-26
@ddimonn8080

You can do this, you can also in the reducer, or actions. Actions with data should be done where it is appropriate, and the relevance depends on what exactly you are doing.
If this logic is only for this component, in order to bring the data from the format in which it is in the store to the format that the component needs, and it makes no sense to put the filtered data in the store - filter in the connection.
If this filtering is needed at the data level in the store (that is, to filter the data that came from somewhere before converting it to the format that should be in the store) - filter in reducers or actions.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question