reactjs - react - ¿Dónde debo manejar la clasificación en la aplicación Redux?
react router (4)
Tengo una acción / reductor / componentes. En uno de mis componentes (volcado de componentes) tengo un Select. Obtengo información sobre qué tipo de filtro de mi tienda. ¿Dónde puedo manejarlo en acción, o reductor?
En mi opinión, el lugar correcto para ordenar los datos no está directamente en los reductores sino en los selectores .
De la redux docs:
Reselect es una biblioteca simple para crear funciones de selector compacta y memorizadas. Los selectores de reselección se pueden usar para calcular de manera eficiente los datos derivados del almacén de Redux.
Actualmente estoy usando selectores para filtrar y ordenar datos.
- No hay repetición de datos en el estado. No tiene que almacenar una copia del artículo ordenada por una forma específica.
- Los mismos datos se pueden utilizar en diferentes componentes, cada uno con una función de selección diferente para ordenar, por ejemplo.
- Puede combinar el selector aplicando muchos cálculos de datos utilizando el selector que ya tiene en la aplicación.
- Si lo haces bien, tus selectores serán puras funciones, entonces puedes probarlas fácilmente.
- Utilice el mismo selector en muchos componentes.
Guardo los elementos, sortKey y sortKind (asc / desc) en la Tienda Redux.
En mi componente Angular (creo que sería igual para React), obtengo el estado de almacenamiento como un Observable para que pueda mostrar los elementos, sortKey y sortOrder en el UX.
Cuando el usuario hace clic en las columnas de la tabla para cambiar la clave de clasificación (orden), despacho las nuevas claves / orden de clasificación al reductor para el estado.
El reductor realiza la nueva clasificación y devuelve el nuevo estado con los valores actualizados.
El Observable en el componente provoca un evento que actualiza la UX.
Ventaja:
mantener la lógica de clasificación fuera del componente
Al guardar sortKey y sortKind en el estado, puede restaurar con precisión la UX si el usuario actualiza el navegador (uso Redux-LocalStorage para sincronizar)
Como la tienda tiene los elementos ordenados, solo realizarás la clasificación cuando el usuario lo desee activamente.
los elementos ordenados se recuerdan para cuando el usuario puede volver al componente.
Mi reductor ("bizzes" es mi lista de artículos, y uso Immutable.List para almacenar los artículos)
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;
}
}
Y mi componente (uso Ng2-Redux para obtener la tienda como 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);
}
}
Como puede ver, estoy usando una Acción (llamada BizzActions) para hacer el envío real de Redux. Podrías hacerlo en tu componente, pero prefiero separar estas cosas. Por si acaso, aquí están mis BizzActions (un Servicio):
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
}
});
};
}
He estado ordenando mis reductores usando un patrón de diccionario de sección. En otras palabras, ordeno mis artículos por encabezados, digo una fecha y luego almaceno los objetos en arreglos por la clave de fecha:
sectionHeaders: ["Monday", "Tuesday"],
dict:{
Monday: [{obj1},{obj2},{obj3}],
Tuesday: [{obj4}],
}
Luego utilizo este dict en React Native para poblar mi ListView porque ListView excepto este formato de objeto para cloneWithRowsAndSections
elementos con secciones usando el método cloneWithRowsAndSections
.
Esta es una optimización de rendimiento porque mi clasificación no es trivial. Tengo que hacer comparaciones profundas y de esta manera solo lo hago una vez cuando poblo la tienda por primera vez, y no cada vez que renderizo la escena.
También he jugado un poco usando un diccionario por ID y almacenando solo las ID en el orden ordenado en lugar de los objetos reales.
Sin embargo, existen concesiones para esto, ya que la actualización es más compleja y usted tiene que decidir cuándo eliminar los encabezados de sección si un elemento se elimina de una sección.
Puede ordenar los datos cuando @connect -ing su componente React con la tienda 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 documentación de Redux muestra un caso similar en el ejemplo de Todo: http://rackt.org/redux/docs/basics/UsageWithReact.html