Только начал изучать RxJS и ему это нравится, но есть одна проблема, которую я не могу решить.
Как это должно работать: вы можете искать по ключевым словам. Но сначала ключевые слова должны быть отправлены на сервер для анализа, затем кешировать результат анализа в магазине, а результат анализа используется для поискового запроса.
Я узнал, как получить и сохранить результат анализа ключевых слов и как отправить поисковый запрос, но не могу найти, как это объединить. Главная проблема для меня - как мне проверить, есть ли ключевые слова в магазине chach, а затем просто пропустить первую часть и отправить поисковый запрос.
const initialState = {
keywords: {}, // keywords cache
query: [], // current query
items: [], // search result
error: null,
};
const reducers = {
setQuery: (state, { payload }) => {
state.query = payload;
},
setItems: (state, { payload }) => {
state.items = payload;
},
setError: (state, { payload }) => {
state.error = payload;
},
addKeyword: (state, { payload }) => {
state.keywords[payload.keyword] = payload;
},
};
const search = createSlice({
initialState,
reducers,
});
const { actions } = search;
const setQueryEpic = (action$, state$) => action$.pipe(
ofType(actions.setQuery.type),
// This part is skipped if all keywords already in state
flatMap(({ payload }) => payload),
filter(keyword => !state$.value.keywords[keyword]),
mergeMap(
keyword => ajax
.getJSON(`/analyze/keyword?keyword=${keyword}`),
),
map(keywordData => actions.addKeyword(keywordData)),
// This part does request and set search result. Should wait for keywords analyze
switchMap((action) => {
const state = state$.value
const params = {
keywords_vectors: state.query.map(queryKeywords => state.keywords[queryKeywords].vector),
};
return ajax
.post(`/search`, JSON.stringify(params))
.pipe(
map(data => data.response.items),
);
}),
map(items => actions.setItems(items)),
catchError(error => of(actions.setError(error.message))),
);
ОБНОВЛЕНИЕ: Нашли это решение
const setQueryEpic = (action$, state$) => action$.pipe(
ofType(actions.setQuery.type),
flatMap(({ payload }) => {
const requests = payload.map((keyword) => {
if (state$.value.keywords[keyword]) {
// keyword in cache
return of(state$.value.keywords[keyword]);
}
// analyze new keyword
return ajax
.getJSON(`/analyze/keyword?keyword=${keyword}`)
});
// wait for all requests
return forkJoin(requests);
}),
switchMap((keywords) => {
const params = {
keywords_vectors: keywords.map(data => data.vector),
};
// create actions to add keywords to cache
const addKeywordActions = keywords
.filter(data => !state$.value.keywords[data.keyword])
.map(data => actions.addKeyword(data));
return ajax
.post(`/search`, JSON.stringify(params))
.pipe(
map(data => data.response.items),
// combine all actions
map(items => [actions.setItems(items), ...addKeywordActions]),
);
}),
flatMap(action => action),
catchError(error => of(actions.setError(error.message))),
);
Но мне не нравится получать addKeywordActions из внешней области видимости, и если вы отправляете несколько поисковых запросов switchMap
сбрасывает всю эту цепочку. Было бы хорошо сохранить данные ключевых слов