I have an action / reducer / components. In one of my components (component dump) I have a Select. I get information on what type of filter my store. Where can I handle it in action, or reducer?
I save the items, sortKey and sortKind (asc/desc) in the Redux Store.
In my Angular component (I believe would be same for React), I get the store state as an Observable so that I can display the items, sortKey and sortOrder in the UX.
When the user clicks on the table columns to change sort key (order) I dispatch the new keys/sort order to reducer for the state.
The reducer then performs the new sorting, and returns the new state with the updated values.
The Observable in the component thus sparks an event which updates the UX.
Advantage:
keep sorting logic out of the component
by saving the sortKey and sortKind in the state, you can restore precisely the UX if the user refreshes the browser (I use Redux-LocalStorage to sync)
as the store has the sorted items, you'll only perform sorting when the user actively wants it.
the sorted items are remembered for when the user might return to the component.
My reducer ( "bizzes" is my items list, and I use Immutable.List to store the items)
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;
}
}
And my component ( I use Ng2-Redux to get the store as 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);
}
}
As you can see, I am using an Action (called BizzActions) to do the actual Redux dispatch. You could do it in your component, but I prefer to separate these things. For good measure, here's my BizzActions (a Service):
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
}
});
};
}
IMO, the right place to sort data is not directly in the reducers but in the selectors.
From redux docs:
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.
I'm currently using selectors to filter and sort data.
- No data repetition in the state. You don't have to store a copy of the item sorted by one specific way.
- The same data can be used in different components, each one using a different selector function to sort for example.
- You can combine selector applying many data computations using selector that you already have in the application.
- If you do right, your selectors will be pure functions, then you can easily test them.
- Use the same selector in many components.
Module build failed: SyntaxError: Unexpected character ''
, –
Synchronism I save the items, sortKey and sortKind (asc/desc) in the Redux Store.
In my Angular component (I believe would be same for React), I get the store state as an Observable so that I can display the items, sortKey and sortOrder in the UX.
When the user clicks on the table columns to change sort key (order) I dispatch the new keys/sort order to reducer for the state.
The reducer then performs the new sorting, and returns the new state with the updated values.
The Observable in the component thus sparks an event which updates the UX.
Advantage:
keep sorting logic out of the component
by saving the sortKey and sortKind in the state, you can restore precisely the UX if the user refreshes the browser (I use Redux-LocalStorage to sync)
as the store has the sorted items, you'll only perform sorting when the user actively wants it.
the sorted items are remembered for when the user might return to the component.
My reducer ( "bizzes" is my items list, and I use Immutable.List to store the items)
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;
}
}
And my component ( I use Ng2-Redux to get the store as 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);
}
}
As you can see, I am using an Action (called BizzActions) to do the actual Redux dispatch. You could do it in your component, but I prefer to separate these things. For good measure, here's my BizzActions (a Service):
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
}
});
};
}
You could sort the data when @connect -ing your React component with the Redux store:
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;
}
}
The Redux documentation shows a similar case in the Todo example: https://redux.js.org/basics/usage-with-react
I've been sorting my reducers using a section dictionary pattern. In other words, I sort my items by headers, say a date, and then store the objects in arrays by the date key:
sectionHeaders: ["Monday", "Tuesday"],
dict:{
Monday: [{obj1},{obj2},{obj3}],
Tuesday: [{obj4}],
}
Then I use this dict in React Native to populate my ListView because ListView will except this object format to render Items with Sections using the cloneWithRowsAndSections
method.
This is a performance optimization because my sorting is not trivial. I have to make deep comparisons and this way I only do it once when I first populate the store, and not every time I render the scene.
I've also played around with using a dictionary by ID and storing only the IDs in the sorted dict instead of the actual objects.
There are trade offs though for this, in that updating is more complex and you have to decide when to remove section headers if an item is removed from a section.
© 2022 - 2024 — McMap. All rights reserved.