Задать вопрос
ddimonn8080
@ddimonn8080

Правильно ли обрабатывать данные в контейнере React компонента?

Здраствуйте, есть компонент
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;

и его контейнер в котором происходит фильтрация и прочая обработка данных.
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);

соответственно
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});
        });
    };
};

и
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;
    }
}

Правильно ли проводить фильтрацию в контейнере или это нужно делать в reducer или в actions?

Спасибо.
  • Вопрос задан
  • 77 просмотров
Подписаться 1 Простой Комментировать
Решения вопроса 2
Robur
@Robur
Знаю больше чем это необходимо
Можно и так, можно и в reducer, или actions. Действия с данными делать надо там где это уместно, а уместность зависит от того что именно вы делаете.

Если эта логика только для данного компонента, чтобы привести данные из формата в котором они лежат в сторе к формату который нужен компоненту, и нет смысла класть фильтрованные данные в стор - фильтруйте в коннекте.
Если эта фильтрация нужна на уровне данных в сторе (то есть профильтровать данные которые пришли откуда-то перед тем как их привести в формат который должен лежать в сторе) - фильтруйте в редьюсерах или экшенах.
Ответ написан
Комментировать
tsepen
@tsepen
Frontend developer
Я стараюсь всю логику выносить из контейнеров, контейнеры просто получают данные и раскидывают их по компонентам. Вся логика в экшенах или выношу в отдельные утилиты
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы