reactjs - redireccionar - react router redux
Disparar acciones de Redux en respuesta a las transiciones de ruta en React Router (2)
Bien, eventualmente encontré una respuesta en la página de Github de Redux, así que la publicaré aquí. Espero que le ahorre a alguien algo de dolor.
@deowk Hay dos partes en este problema, yo diría. El primero es que componentWillReceiveProps () no es una forma ideal de responder a los cambios de estado, principalmente porque te obliga a pensar imperativamente, en lugar de reaccionar como lo hacemos con Redux. La solución es almacenar la información actual de su enrutador (ubicación, parámetros, consulta) dentro de su tienda. Entonces todo su estado está en el mismo lugar, y puede suscribirse a él utilizando la misma API de Redux que el resto de sus datos.
El truco es crear un tipo de acción que se active siempre que cambie la ubicación del enrutador. Esto es fácil en la próxima versión 1.0 de React Router:
// routeLocationDidUpdate() is an action creator
// Only call it from here, nowhere else
BrowserHistory.listen(location => dispatch(routeLocationDidUpdate(location)));
Ahora el estado de su tienda siempre estará sincronizado con el estado del enrutador. Eso soluciona la necesidad de reaccionar manualmente a los cambios de parámetros de consulta y setState () en su componente anterior, solo use el conector de Redux.
<Connector select={state => ({ filter: getFilters(store.router.params) })} />
La segunda parte del problema es que necesita una forma de reaccionar a los cambios de estado de Redux fuera de la capa de vista, por ejemplo, para activar una acción en respuesta a un cambio de ruta. Puede continuar usando componentWillReceiveProps para casos simples como el que describe, si lo desea.
Sin embargo, para cualquier cosa más complicada, recomiendo usar RxJS si está abierto a ello. Esto es exactamente para lo que están diseñados los observables: flujo de datos reactivos.
Para hacer esto en Redux, primero cree una secuencia observable de estados de la tienda. Puede hacer esto usando observableFromStore () de rx.
EDITAR COMO SUGERIDO POR CNP
import { Observable } from ''rx''
function observableFromStore(store) {
return Observable.create(observer =>
store.subscribe(() => observer.onNext(store.getState()))
)
}
Entonces es solo cuestión de usar operadores observables para suscribirse a cambios de estado específicos. Aquí hay un ejemplo de redireccionamiento desde una página de inicio de sesión después de un inicio de sesión exitoso:
const didLogin$ = state$
.distinctUntilChanged(state => !state.loggedIn && state.router.path === ''/login'')
.filter(state => state.loggedIn && state.router.path === ''/login'');
didLogin$.subscribe({
router.transitionTo(''/success'');
});
Esta implementación es mucho más simple que la misma funcionalidad usando patrones imperativos como componentDidReceiveProps ().
Estoy usando react-router y redux en mi última aplicación y me enfrento a un par de problemas relacionados con los cambios de estado requeridos en función de los parámetros y consultas de URL actuales.
Básicamente tengo un componente que necesita actualizar su estado cada vez que cambia la url. El estado se pasa a través de accesorios por redux con el decorador así
@connect(state => ({
campaigngroups: state.jobresults.campaigngroups,
error: state.jobresults.error,
loading: state.jobresults.loading
}))
En este momento estoy usando el método del ciclo de vida del componente WillReceiveProps para responder a los cambios de url que provienen de react-router, ya que react-router pasará nuevos accesorios al controlador cuando la url cambie en this.props.params y this.props.query: el El problema principal con este enfoque es que estoy activando una acción en este método para actualizar el estado, que luego pasa y pasa nuevos accesorios al componente que activará el mismo método de ciclo de vida nuevamente, por lo que básicamente estoy creando un ciclo sin fin, actualmente estoy configurando un variable de estado para evitar que esto suceda.
componentWillReceiveProps(nextProps) {
if (this.state.shouldupdate) {
let { slug } = nextProps.params;
let { citizenships, discipline, workright, location } = nextProps.query;
const params = { slug, discipline, workright, location };
let filters = this._getFilters(params);
// set the state accroding to the filters in the url
this._setState(params);
// trigger the action to refill the stores
this.actions.loadCampaignGroups(filters);
}
}
¿Existe un enfoque estándar para desencadenar acciones basadas en transiciones de ruta O puedo tener el estado de la tienda directamente conectado al estado del componente en lugar de pasarlo a través de accesorios? He intentado usar el método estático willTransitionTo pero no tengo acceso al this.props.dispatch allí.
Como se mencionó anteriormente, la solución tiene dos partes:
1) Enlace la información de enrutamiento al estado
Para eso, todo lo que tiene que hacer es configurar react-router-redux . Sigue las instrucciones y estarás bien.
Después de que todo esté configurado, debe tener un estado de
routing
, como este:
2) Observa los cambios de ruta y activa tus acciones
En algún lugar de su código, debería tener algo como esto ahora:
// find this piece of code
export default function configureStore(initialState) {
// the logic for configuring your store goes here
let store = createStore(...);
// we need to bind the observer to the store <<here>>
}
Lo que desea hacer es observar los cambios en la tienda, para que pueda
dispatch
acciones cuando algo cambie.
Como @deowk mencionó, puede usar
rx
, o puede escribir su propio observador:
reduxStoreObserver.js
var currentValue;
/**
* Observes changes in the Redux store and calls onChange when the state changes
* @param store The Redux store
* @param selector A function that should return what you are observing. Example: (state) => state.routing.locationBeforeTransitions;
* @param onChange A function called when the observable state changed. Params are store, previousValue and currentValue
*/
export default function observe(store, selector, onChange) {
if (!store) throw Error(''/'store/' should be truthy'');
if (!selector) throw Error(''/'selector/' should be truthy'');
store.subscribe(() => {
let previousValue = currentValue;
try {
currentValue = selector(store.getState());
}
catch(ex) {
// the selector could not get the value. Maybe because of a null reference. Let''s assume undefined
currentValue = undefined;
}
if (previousValue !== currentValue) {
onChange(store, previousValue, currentValue);
}
});
}
Ahora, todo lo que tiene que hacer es usar
reduxStoreObserver.js
que acabamos de escribir para observar los cambios:
import observe from ''./reduxStoreObserver.js'';
export default function configureStore(initialState) {
// the logic for configuring your store goes here
let store = createStore(...);
observe(store,
//if THIS changes, we the CALLBACK will be called
state => state.routing.locationBeforeTransitions.search,
(store, previousValue, currentValue) => console.log(''Some property changed from '', previousValue, ''to'', currentValue)
);
}
El código anterior hace que se llame a nuestra función cada vez que locationBeforeTransitions.search cambia en el estado (como resultado de la navegación del usuario). Si lo desea, puede observar la cadena de consulta que y así sucesivamente.
Si desea activar una acción como resultado de los cambios de enrutamiento, todo lo que tiene que hacer es
store.dispatch(yourAction)
dentro del controlador.