Запуск действий Redux в ответ на переходы маршрута в маршрутизаторе React
Я использую react-router и redux в моем последнем приложении, и я столкнулся с несколькими проблемами, связанными с изменениями состояния, необходимыми на основе текущих параметров url и запросов.
в основном у меня есть компонент, который должен обновлять его состояние каждый раз, когда меняется url. Состояние передается через реквизит redux с декоратором, как так
@connect(state => ({
campaigngroups: state.jobresults.campaigngroups,
error: state.jobresults.error,
loading: state.jobresults.loading
}))
на данный момент я использую метод жизненного цикла componentWillReceiveProps для ответа на изменения url, поступающие из реагировать-маршрутизатор с реагировать-маршрутизатор пройдет новый реквизит в обработчике при изменении URL-адреса в этом.реквизит.парамы и это.реквизит.запрос-основная проблема с этим подходом заключается в том, что я запускаю действие в этом методе для обновления состояния, которое затем переходит и передает новые реквизиты компонента, который снова запускает тот же метод жизненного цикла, поэтому в основном создавая бесконечный цикл, в настоящее время я устанавливаю переменную состояния, чтобы остановить это.
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);
}
}
есть ли стандартная подход к триггерным действиям базируется на переходах маршрута или я могу иметь состояние хранилища, непосредственно связанное с состоянием компонента, а не передавать его через реквизит? Я попытался использовать willTransitionTo статический метод, но у меня нет доступа к этому.реквизит.отправь туда.
2 ответа:
хорошо, в конце концов я нашел ответ на странице GitHub redux, поэтому опубликую его здесь. Надеюсь, это спасет кого-то от боли.
@deowk есть две части этой проблемы, я бы сказал. Во - первых, componentWillReceiveProps() не является идеальным способом реагирования на изменения состояния-в основном потому, что он заставляет вас думать императивно, а не реактивно, как мы делаем с Redux. Решение состоит в том, чтобы хранить текущую информацию о маршрутизаторе (местоположение, параметры, запрос) внутри вашего магазина. Тогда все ваше состояние находится в том же месте, и вы можете подписаться на него, используя тот же API Redux, что и остальные ваши данные.
хитрость состоит в том, чтобы создать тип действия, который срабатывает при каждом изменении местоположения маршрутизатора. Это легко в предстоящей версии 1.0 React Router:
// routeLocationDidUpdate() is an action creator // Only call it from here, nowhere else BrowserHistory.listen(location => dispatch(routeLocationDidUpdate(location)));
теперь состояние вашего магазина всегда будет синхронизировано с состоянием маршрутизатора. Это устраняет необходимость вручную реагировать на изменения параметров запроса и setState () в вашем компоненте выше-просто используйте Redux Соединитель.
<Connector select={state => ({ filter: getFilters(store.router.params) })} />
вторая часть проблемы заключается в том, что вам нужен способ реагировать на изменения состояния Redux за пределами слоя представления, например, запускать действие в ответ на изменение маршрута. Вы можете продолжать использовать componentWillReceiveProps для простых случаев, таких как тот, который вы описываете, если хотите.
для чего-то более сложного, хотя, я рекомендую использовать RxJS, если вы открыты для него. Это именно то, для чего предназначены наблюдаемые-реактивные данные поток.
чтобы сделать это в Redux, сначала создайте наблюдаемую последовательность состояний хранилища. Вы можете сделать это с помощью observableFromStore () rx.
РЕДАКТИРОВАТЬ ПО ПРЕДЛОЖЕНИЮ CNP
import { Observable } from 'rx' function observableFromStore(store) { return Observable.create(observer => store.subscribe(() => observer.onNext(store.getState())) ) }
тогда это просто вопрос использования наблюдаемых операторов для подписки на определенные изменения состояния. Вот пример переориентации со страницы входа после успешного входа в систему:
const didLogin$ = state$ .distinctUntilChanged(state => !state.loggedIn && state.router.path === '/login') .filter(state => state.loggedIn && state.router.path === '/login'); didLogin$.subscribe({ router.transitionTo('/success'); });
эта реализация намного проще, чем та же функциональность с использованием императивных шаблонов, таких как componentDidReceiveProps().
Как упоминалось ранее, решение состоит из двух частей:
1) свяжите информацию о маршруте с состоянием
для этого все, что вам нужно сделать, это установить react-router-redux. Следуйте инструкциям, и все будет хорошо.
после того как все установлено, вы должны иметь
routing
государство, как это:2) наблюдайте за изменениями маршрута и запускайте свои действия
где-то в коде вы должны есть что-то вроде этого сейчас:
// 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>> }
что вы хотите сделать, это наблюдать за изменениями в магазине, так что вы можете
dispatch
действия, когда что-то меняется.как упоминал @deowk, вы можете использовать
rx
, или вы можете написать свой собственный наблюдатель: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); } }); }
теперь, все, что вам нужно сделать, это использовать
reduxStoreObserver.js
мы просто написали, чтобы наблюдать за изменениями: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) ); }
приведенный выше код делает нашу функцию быть вызывается каждый раз locationBeforeTransitions.поиск изменений в состоянии (в результате навигации пользователя). Если вы хотите, вы можете наблюдать строку запроса que и так далее.
если вы хотите вызвать действие в результате изменения маршрута, все, что вам нужно сделать, это
store.dispatch(yourAction)
внутри обработчика.