我应该在哪里处理 Redux App 中的排序?

Where should I handle sorting in Redux App?

我有一个动作/减速器/组件。在我的一个组件(组件转储)中,我有一个 Select。我得到关于我的商店是什么类型的过滤器的信息。我在哪里可以处理它,或者 reducer?

您可以在使用 Redux 存储 @connect -ing 您的 React 组件时对数据进行排序:

function mapStateToProps(state) {
   var items = state.items.slice(0);
   items.sort()
   return {
     items: items
   }
}

@connect(mapStoreToProps)
class MyComponent extends React.Component {
   render() {
      var items = this.props.items;
   }
}

Redux 文档在 Todo 示例中显示了类似的情况:https://redux.js.org/basics/usage-with-react

我将项目、sortKey 和 sortKind (asc/desc) 保存在 Redux Store 中。

在我的 Angular 组件中(我相信 React 也是如此),我将存储状态作为 Observable 获取,以便我可以在 UX 中显示项目、sortKey 和 sortOrder。

当用户单击 table 列以更改排序键(顺序)时,我将新的 keys/sort 订单发送到状态的缩减器。

reducer 然后执行新的排序,return使用更新后的值对新状态进行排序。

因此,组件中的 Observable 会触发更新 UX 的事件。

优点:

  • 将排序逻辑保留在组件之外

  • 通过保存状态中的sortKey和sortKind,如果用户刷新浏览器(我使用Redux-LocalStorage来同步),你可以精确地恢复UX

  • 由于商店有已排序的项目,您将仅在用户主动需要时执行排序。

  • 当用户可能 return 组件时,系统会记住已排序的项目。

我的reducer("bizzes"是我的物品列表,我用Immutable.List来存放物品)

import { List }                     from 'immutable';
import { IBizz, IBizzState }   from './bizz.types';
import { BIZZES_SET, BIZZES_SORT}    from 'store/constants';

const SORT_ASC = 'asc';
const SORT_DESC = 'desc';

const defaultSortKey = 'serialNo';
const defaultSortOrder = SORT_ASC;

const INITIAL_STATE: IBizzState =  {
    bizzes: List([]),
    sortKey: defaultSortKey,
    sortOrder: defaultSortOrder
};

export function bizzReducer(state: IBizzState = INITIAL_STATE, action: any): IBizzState {

    switch (action.type) {

        case BIZZES_SET:
            return {
                bizzes: List(action.payload.bizzes),
                sortKey: action.payload.sortKey || defaultSortKey,
                sortOrder: action.payload.sortOrder || defaultSortOrder
            };

        case BIZZES_SORT:
            let sortKey = action.payload.sortKey || defaultSortKey;

            if(sortKey === state.sortKey) {
                state.sortOrder = state.sortOrder === SORT_ASC ? SORT_DESC : SORT_ASC;
            }

            return {
                bizzes: List(state.bizzes.sort( (a, b) => { 
                    if( a[sortKey] < b[sortKey] ) return state.sortOrder === SORT_ASC ? -1 : 1;
                    if( a[sortKey] > b[sortKey] ) return state.sortOrder === SORT_ASC ? 1: -1;
                    return 0;
                })),
                sortKey: sortKey,
                sortOrder: state.sortOrder
            };
        default: return state;
    }
}

我的组件(我使用 Ng2-Redux 将商店作为 Observables):

import { Component, OnInit, OnDestroy, ChangeDetectionStrategy } from '@angular/core';
import { select } from 'store';
import { BizzActions } from 'actions/index';

@Component({
    selector: 'bizzlist',
    templateUrl: './bizz-list.html',
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class BizzListComponent implements OnInit {


    @select([ 'bizzState']) bizzState$;

    public sortOrder: string;
    public sortKey: string;
    public bizzes = [];
    private bizzStateSubscription; 


    constructor( 
        public bizzActions: BizzActions
    ) { }

    ngOnInit() {
        this.bizzStateSubscription = this.bizzState$.subscribe( bizzState => {
            this.bizzes = bizzState.bizzes;
            this.sortKey = bizzState.sortKey;
            this.sortOrder = bizzState.sortOrder;
        });
     }

    ngOnDestroy() {
        this.bizzStateSubscription.unsubscribe();
    }



    public sortBizzes(key) {
        this.bizzActions.sortBizzes(key); 
    }
}

如您所见,我正在使用一个 Action(称为 BizzActions)来执行实际的 Redux 调度。您可以在您的组件中完成,但我更喜欢将这些东西分开。作为衡量标准,这是我的 BizzActions(一项服务):

import { Injectable }           from '@angular/core';
import { NgRedux, IAppState }   from 'store';
import { 
    BIZZES_SET,
    BIZZES_SORT 
} from 'store/constants';

@Injectable()
export class BizzActions {

    constructor (private ngRedux: NgRedux<IAppState>) {}

    public setBizzes = (bizzes: any) => {
        return this.ngRedux.dispatch({
            type: BIZZES_SET,
            payload: {
                bizzes: bizzes
            }
        });
    };

    public sortBizzes = (key:string) => {
        return this.ngRedux.dispatch({
            type: BIZZES_SORT,
            payload: {
                sortKey: key
            }
        });
    };

}

我一直在使用节字典模式对我的减速器进行排序。换句话说,我按 headers 对项目进行排序,说一个日期,然后按日期键将 object 存储在数组中:

sectionHeaders: ["Monday", "Tuesday"],
dict:{
    Monday: [{obj1},{obj2},{obj3}],
    Tuesday: [{obj4}],
}

然后我在 React Native 中使用这个字典来填充我的 ListView,因为 ListView 将使用 cloneWithRowsAndSections 方法使用 object 格式呈现带有部分的项目。

这是一项性能优化,因为我的排序并不简单。我必须进行深入比较,这样我只在第一次填充商店时进行一次比较,而不是每次渲染场景时都进行比较。

我也试过按 ID 使用字典,并只将 ID 存储在排序后的字典中,而不是实际的 objects。

虽然这样做有一些折衷,因为更新更复杂,如果从某个部分中删除了某个项目,您必须决定何时删除部分 headers。

IMO,排序数据的正确位置不是直接在 reducer 中,而是在 selectors.

来自 redux 文档:

Computing Derived Data

Reselect is a simple library for creating memoized, composable selector functions. Reselect selectors can be used to efficiently compute derived data from the Redux store.

我目前正在使用选择器来过滤和排序数据。

  1. 状态下无数据重复。您不必存储按特定方式排序的项目的副本。
  2. 可以在不同的组件中使用相同的数据,例如,每个组件使用不同的选择器函数进行排序。
  3. 您可以使用应用程序中已有的选择器来组合应用许多数据计算的选择器。
  4. 如果你做对了,你的选择器将是纯函数,那么你可以很容易地测试它们。
  5. 在多个组件中使用相同的选择器。