Привет, ребята,
Я использую response-router и redux в своем последнем приложении, и я столкнулся с несколькими проблемами, связанными с изменениями состояния, необходимыми на основе параметров и запросов текущего URL-адреса.
В основном у меня есть компонент, который должен обновлять свое состояние каждый раз, когда изменяется URL-адрес. Состояние передается через реквизиты с помощью redux с таким декоратором
@connect(state => ({
campaigngroups: state.jobresults.campaigngroups,
error: state.jobresults.error,
loading: state.jobresults.loading
}))
В настоящий момент я использую метод жизненного цикла componentWillReceiveProps
для ответа на изменения URL-адреса, поступающие от response-router, поскольку response-router будет передавать новые реквизиты обработчику, когда URL-адрес изменяется в this.props.params
и this.props.query
- основная проблема с этим подходом заключается в том, что я запускаю действие в этом методе для обновления состояния, которое затем переходит и передает новые реквизиты компоненту, который снова запускает тот же метод жизненного цикла - поэтому в основном создается бесконечный цикл, в настоящее время я устанавливаю переменную состояния, чтобы этого не произошло.
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
но у меня нет доступа к this.props.dispatch.
@deowk Вы можете сравнить this.props
с nextProps
чтобы увидеть, изменились ли соответствующие данные. Если он не изменился, вам не нужно снова запускать действие и вы можете выйти из бесконечного цикла.
@johanneslumpe : В моем случае группы кампаний - это довольно большая коллекция объектов, кажется неэффективным использование глубокого сравнения объектов в качестве условия таким образом?
@deowk, если вы используете неизменяемые данные, вы можете просто сравнить ссылки
@deowk Я бы сказал, что у этой проблемы две части. Первое , что componentWillReceiveProps()
не является идеальным способом для реагирования на изменения состояния - в основном потому , что заставляет вас думать , императивно, а не реактивно , как мы делаем с Redux. Решение состоит в том, чтобы хранить информацию о вашем текущем маршрутизаторе (местоположение, параметры, запрос) _внутри_ вашего магазина. Тогда все ваше состояние находится в одном месте, и вы можете подписаться на него, используя тот же Redux API, что и остальные ваши данные.
Уловка состоит в том, чтобы создать тип действия, который срабатывает при изменении местоположения маршрутизатора. В грядущей версии 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.
<Connector select={state => ({ filter: getFilters(store.router.params) })} />
Вторая часть проблемы заключается в том, что вам нужен способ реагировать на изменения состояния Redux за пределами уровня представления, например, запускать действие в ответ на изменение маршрута. Вы можете продолжать использовать componentWillReceiveProps для простых случаев, подобных описанному вами, если хотите.
Однако для чего-то более сложного я рекомендую использовать RxJS, если вы открыты для этого. Именно для этого и созданы наблюдаемые объекты - реактивный поток данных.
Чтобы сделать это в Redux, сначала создайте наблюдаемую последовательность состояний хранилища. Вы можете сделать это с помощью команды redux-rx observableFromStore()
.
import { observableFromStore } from 'redux-rx';
const state$ = observableFromStore(store);
Тогда это просто вопрос использования наблюдаемых операторов для подписки на определенные изменения состояния. Вот пример перенаправления со страницы входа после успешного входа в систему:
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()
.
Надеюсь, это поможет!
Нам это нужно в новых документах. Так хорошо написано.
@acdlite : Большое спасибо, ваш совет мне очень помог, хотя я борюсь со следующим:> изменение состояния в магазине путем запуска создателя действия в ответ на изменение URL-адреса.
Я хочу вызвать создателя действия в статическом методе willTransitionTo, поскольку это кажется единственным вариантом в response-router v0.13.3
class Results extends Component {
..........
}
Results.willTransitionTo = function (transition, params, query) {
// how do I get a reference to dispatch here?
};
@deowk Я предполагаю, что вы вызываете dispatch
непосредственно в экземпляре redux:
const redux = createRedux(stores);
BrowserHistory.listen(location => redux.dispatch(routeLocationDidUpdate(location)));
@deowk В настоящее время я отправляю свои изменения URL в реагирующем маршрутизаторе 0.13.3 следующим образом:
Router.run(routes, Router.HistoryLocation, function(Handler, locationState) {
dispatch(routeLocationDidUpdate(locationState))
React.render(<Handler/>, appEl);
});
@acdlite действительно хорошо объяснил! : +1:
Согласитесь, это должно быть и в новых документах :)
Как примечание, BrowserHistory.history()
изменилось на BrowserHistory.addChangeListener()
.
https://github.com/rackt/react-router/blob/master/modules/BrowserHistory.js#L57
РЕДАКТИРОВАТЬ:
Что может выглядеть так:
history.addChangeListener(() => store.dispatch(routeLocationDidUpdate(location)));
А как насчет начального состояния этого редуктора, @pburtchaell?
У меня следующая установка:
// client.js
class App extends Component {
constructor(props) {
super(props);
this.history = new HashHistory();
}
render() {
return (
<Provider store={store}>
{renderRoutes.bind(null, this.history)}
</Provider>
);
}
}
// routes.js
export default function renderRoutes(history) {
// When the route changes, dispatch that information to the store.
history.addChangeListener(() => store.dispatch(routeLocationDidUpdate(location)));
return (
<Router history={history}>
<Route component={myAppView}>
<Route path="/" component={myHomeView} />
</Route>
</Router>
);
};
Это запускает создателя действия routeLocationDidUpdate()
как при каждом изменении маршрута, так и при начальной загрузке приложения.
@pburtchaell можешь поделиться своими routeLocationDidUpdate
?
@gyzerok Конечно. Это создатель действий.
// actions/location.js
export function routeLocationDidUpdate(location) {
return {
type: 'LOCATION_UPDATE',
payload: {
...location,
},
};
}
@pburtchaell, поэтому в вашем примере я не понимаю, где вы берете данные, необходимые для определенного маршрута
Вот более полная версия моего примера выше:
https://github.com/acdlite/redux-react-router/blob/master/README.md#bonus -reacting-to-state-changes-with-redux-rx
Я предполагаю, что @pburtchaell будет использовать асинхронную версию этого кода для получения данных (из вызова REST и т. Д.). Я не уверен в том, что тогда? Я предполагаю, что затем он переходит в магазин, но будет ли этот магазин местоположением, которое я тогда
<strong i="7">@connect</strong>
в, скажем, в
<Comments />
который уже был бы «подключен» (подписан) на commentStore? Или просто добавить прописать эти действия по местоположению в комментариях Store?
URL как
/comments/123
всегда будет «привязан» к компоненту комментариев, как мне его повторно использовать? Извините, если это тупо, здесь очень запутались. Я смог найти убедительный пример.
Я также борюсь с этим, выясняя, как вызвать мой статический метод fetchData(dispatch, params)
(статический, потому что сервер вызывает его для отправки асинхронных действий FETCH перед начальным рендерингом).
Поскольку ссылка на dispatch
существует в контексте, я думаю, что самый чистый способ получить это для вызова fetchData
на стороне клиента - это Connector
-подобный компонент, который вызывает fetchData
или shouldFetchData
, либо обратный вызов select
получит ссылку на dispatch
вместе с state
, чтобы мы могли проверить текущую состояние store
и отправка действий FETCH
мере необходимости.
Последнее более лаконично, но нарушает тот факт, что select
должна оставаться чистой функцией. Я изучу первое.
Мое решение таково, хотя оно полностью адаптировано к моей настройке (статический метод fetchData(dispatch, state)
который отправляет async _FETCH
actions), он будет вызывать любые переданные ему обратные вызовы с соответствующими свойствами: https: // gist.github.com/grrowl/6cca2162e468891d8128 -
Нам обязательно нужно добавить документы! Возможно в разделе «Использование с реактивным роутером».
У нас будет официальный способ сделать это после выпуска 1.0.
Приятно слышать @gaearon.
Тогда это просто вопрос использования наблюдаемых операторов для подписки на определенные изменения состояния.
У меня есть наблюдаемый поток, настроенный для синхронизации моего магазина с любыми изменениями маршрута, и у меня есть настройка потока, чтобы смотреть мой магазин. Меня интересует переход на новый маршрут, когда значение изменяется в моем магазине, в частности, когда оно изменяется с undefined
на допустимую строку в результате успешной отправки формы в другом месте моего приложения.
Два потока настроены и работают, и хранилище обновляется, но я новичок в Rx, и у меня проблемы с наблюдаемым запросом ... какие-либо указатели? Я на правильном пути? Уверен, что это как-то связано с distinctUntilChanged
но сейчас это моя лапша, любая помощь приветствуется :)
РЕДАКТИРОВАТЬ: Ack! Извините, ответил на свой вопрос. Псевдокод ниже. Дай мне знать, если это выглядит ужасно?
const didChangeProject$ = state$
.distinctUntilChanged(state => state.project._id)
.filter(state => typeof state.project._id !== 'undefined');
Закрытие в пользу №177. Когда мы перейдем к документированию маршрутизации, нам нужно будет охватить все сценарии: перенаправление при изменении состояния, перенаправление при обратном вызове запроса и т. Д.
Я знаю, что это закрыто ... но с React 0.14 и различными зависимостями 1.0 в бета-версии прямо сейчас, есть ли обновление для этого, а если нет, может ли быть учебник по новым возможностям в React 0.14, response-router, redux и т. Д. написано? Кажется, многие из нас пытаются быть в курсе предстоящих изменений, одновременно изучая / понимая все это. Я знаю, что все в движении (каламбур .. э-э .. не предназначено), но мне кажется, что я вижу фрагменты из разных примеров, которые плохо сочетаются друг с другом, и через несколько дней я все еще не могу заставить мое обновленное приложение работать с маршрутизацией. Скорее всего, это моя собственная вина, поскольку я все еще немного борюсь с пониманием того, как все это работает, просто надеюсь, что скоро выйдет обновленное руководство с последними материалами.
Пока не будет учебника, не стесняйтесь смотреть на examples/real-world
котором есть маршрутизация. На данный момент он не является «последним и лучшим», но работает нормально.
Самый полезный комментарий
@deowk Я бы сказал, что у этой проблемы две части. Первое , что
componentWillReceiveProps()
не является идеальным способом для реагирования на изменения состояния - в основном потому , что заставляет вас думать , императивно, а не реактивно , как мы делаем с Redux. Решение состоит в том, чтобы хранить информацию о вашем текущем маршрутизаторе (местоположение, параметры, запрос) _внутри_ вашего магазина. Тогда все ваше состояние находится в одном месте, и вы можете подписаться на него, используя тот же Redux API, что и остальные ваши данные.Уловка состоит в том, чтобы создать тип действия, который срабатывает при изменении местоположения маршрутизатора. В грядущей версии 1.0 React Router это легко сделать:
Теперь состояние вашего магазина всегда будет синхронизироваться с состоянием маршрутизатора. Это устраняет необходимость вручную реагировать на изменения параметров запроса и
setState()
в указанном выше компоненте - просто используйте Redux Connector.Вторая часть проблемы заключается в том, что вам нужен способ реагировать на изменения состояния Redux за пределами уровня представления, например, запускать действие в ответ на изменение маршрута. Вы можете продолжать использовать componentWillReceiveProps для простых случаев, подобных описанному вами, если хотите.
Однако для чего-то более сложного я рекомендую использовать RxJS, если вы открыты для этого. Именно для этого и созданы наблюдаемые объекты - реактивный поток данных.
Чтобы сделать это в Redux, сначала создайте наблюдаемую последовательность состояний хранилища. Вы можете сделать это с помощью команды redux-rx
observableFromStore()
.Тогда это просто вопрос использования наблюдаемых операторов для подписки на определенные изменения состояния. Вот пример перенаправления со страницы входа после успешного входа в систему:
Эта реализация намного проще, чем та же функциональность с использованием императивных шаблонов, таких как
componentDidReceiveProps()
.Надеюсь, это поможет!