web-dev-qa-db-fra.com

Où dois-je gérer le tri dans Redux App?

J'ai une action/réducteur/composants. Dans l'un de mes composants (composant dump), j'ai un Select. Je reçois des informations sur le type de filtre de mon magasin. Où puis-je le gérer en action, ou réducteur?

20
Viktor Pavlenko

Je sauve les articles, sortKey et sortKind (asc/desc) dans le magasin Redux.

Dans mon composant Angular (je pense que ce serait la même chose pour React), je reçois l'état du magasin en tant qu'observable afin de pouvoir afficher les éléments, sortKey et sortOrder dans l'UX.

Lorsque l'utilisateur clique sur les colonnes de la table pour modifier la clé de tri (ordre), j'envoie les nouvelles clés/ordre de tri au réducteur de l'état.

Le réducteur effectue ensuite le nouveau tri et renvoie le nouvel état avec les valeurs mises à jour.

L'Observable dans le composant déclenche donc un événement qui met à jour l'UX.

Avantage:

  • garder la logique de tri en dehors du composant

  • en sauvegardant les types sortKey et sortKind dans l'état, vous pouvez restaurer précisément l'UX si l'utilisateur actualise le navigateur (j'utilise Redux-LocalStorage pour la synchronisation)

  • le magasin ayant les éléments triés, vous ne pourrez effectuer le tri que lorsque l'utilisateur le souhaite activement. 

  • les éléments triés sont mémorisés lorsque l'utilisateur peut revenir au composant.

Mon réducteur ("bizzes" est ma liste d'articles et j'utilise Immutable.List pour stocker les articles)

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

Et mon composant (j'utilise Ng2-Redux pour obtenir le magasin en tant qu'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); 
    }
}

Comme vous pouvez le constater, j'utilise une action (appelée BizzActions) pour effectuer la distribution Redux. Vous pouvez le faire dans votre composant, mais je préfère séparer ces choses. Pour faire bonne mesure, voici mon BizzActions (un 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
            }
        });
    };

}
6
Spock

OMI, le bon endroit pour trier les données ne se trouve pas directement dans les réducteurs mais dans les sélecteurs .

De redux docs:

Calcul des données dérivées

Reselect est une bibliothèque simple permettant de créer des fonctions de sélecteur composables et mémoisées. Les sélecteurs de resélection peuvent être utilisés pour calculer efficacement les données dérivées à partir du magasin Redux.

J'utilise actuellement des sélecteurs pour filtrer et trier les données.

  1. Aucune répétition de données dans l'état. Vous n'avez pas besoin de stocker une copie de l'article triée par un moyen spécifique.
  2. Les mêmes données peuvent être utilisées dans différents composants, chacun utilisant un sélecteur différent, par exemple.
  3. Vous pouvez combiner le sélecteur en appliquant de nombreux calculs de données à l'aide du sélecteur que vous avez déjà dans l'application.
  4. Si vous le faites bien, vos sélecteurs seront des fonctions pures, alors vous pourrez facilement les tester.
  5. Utilisez le même sélecteur dans de nombreux composants.

Vous pouvez trier les données lors de la connexion de votre composant React avec le magasin Redux:

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

La documentation Redux montre un cas similaire dans l'exemple de Todo: http://rackt.org/redux/docs/basics/UsageWithReact.html

8
Atanas Korchev

J'ai trié mes réducteurs en utilisant un modèle de dictionnaire de sections. En d'autres termes, je trie mes éléments par en-tête, par exemple une date, puis je stocke les objets dans des tableaux à l'aide de la clé de date:

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

Ensuite, j'utilise ce dictionnaire dans React Native pour renseigner mon ListView, car ListView exceptera ce format d'objet pour rendre les éléments avec des sections à l'aide de la méthode cloneWithRowsAndSections.

Il s’agit d’une optimisation des performances car mon tri n’est pas trivial. Je dois faire des comparaisons approfondies. Ainsi, je ne le fais qu'une fois lorsque je remplis le magasin pour la première fois, et pas à chaque rendu de la scène. 

J'ai également joué avec l'utilisation d'un dictionnaire par identifiant et en stockant uniquement les identifiants dans le dict trié au lieu des objets réels. 

Cependant, il existe des compromis pour cela, dans la mesure où la mise à jour est plus complexe et vous devez décider quand supprimer les en-têtes de section si un élément est supprimé d'une section.

1
ssomnoremac