Подтвердить что ты не робот

Должен ли я использовать один или несколько типов действий для представления этого асинхронного действия?

Я создаю интерфейс для поисковой системы, где почти все действия пользователя должны запускать одно и то же действие async для повторной выборки результатов поиска. Например, если пользователь вводит ключевое слово, тогда нам нужно получить /api/search?q=foo, и, если позже они выберут категорию, мы получаем /api/search?q=foo&categoryId=bar. Первоначально у меня были отдельные типы действий для FETCH_RESULTS, SELECT_CATEGORY, DESELECT_CATEGORY и т.д. Я создал один создатель асинхронного действия для FETCH_RESULTS, но остальные синхронны. Чем больше я думаю об этом, тем в итоге все они нуждаются в повторной выборке результатов из бэкэнд и обновлении состояния приложения на основе ответа от бэкэнда.

Будет ли иметь смысл использовать для каждого изменения один асинхронный action-creator? Или было бы лучше использовать создателей асинхронного действия для каждого отдельного действия пользователя (выбор ключевого слова, категории или фильтра)?

Я думаю, что преимущество гранулярных действий будет состоять в том, чтобы события более точно отражали то, что сделал пользователь (например, пользователь, выбранный в категории), а также заглядывать в полезную нагрузку, чтобы выяснить, что на самом деле изменилось, но все они очень похожи.

4b9b3361

Ответ 1

Это, конечно, что-то, на что вы можете ответить только на основе того, что вы знаете о проекте. Я не думаю, что есть какое-то неотъемлемое преимущество для того, чтобы действия были более гранулированными, а если их нет, это не стоит дополнительных усилий. У меня было бы общее событие FILTER_CHANGED и не беспокоиться о возможности увидеть, что конкретно изменилось - по-видимому, действие не будет сложным, поэтому я не буду много отлаживать действие. По мере того, как состояние фильтра становится более сложным и разнообразным, может возникнуть больше смысла вызывать действия. По умолчанию, однако, я действительно не вижу большого значения.

Ответ 2

Я полностью согласен с ответом Натанса.

Я просто хочу добавить, что для того, чтобы определить, действительно ли действия A и B являются одним или двумя действиями, вам нужно спросить себя: "Если я изменю, как некоторые редукторы реагируют на A, мне также нужно будет изменить, как они реагировать на B?"

Когда обработчики изменяются вместе в коде редуктора, вероятно, они должны быть одним действием. Когда их изменения могут не влиять друг на друга, или если многие редукторы работают только с одним из них, но не с другим, они, вероятно, должны оставаться отдельными.

Ответ 3

Я согласен с Дэном Абрамовым: если текст и категории сильно связаны в вашем интерфейсе, просто стреляйте FETCH_RESULTS с текстом и категориями как полезная нагрузка.

Если виджет ввода текста и выбора категорий не разделяет близкий родительский компонент, сложно запустить FETCH_RESULTS, который содержит текст и категории (если только не передается множество реквизитов вниз по дереву...): вы то нужна гранулярность действия.

Один шаблон, который я нашел полезным, когда нужна такая гранулярность, - это Saga/Образец диспетчера процессов. Я немного написал об этом здесь: fooobar.com/info/140791/...

В принципе, реализация этого на redux означала бы наличие особого рода редуктора, который может вызывать побочные эффекты. Этот редуктор не является чистым, но не имеет целью инициировать визуализацию React, но вместо этого управляет координацией компонентов.

Вот пример того, как я буду реализовывать ваш usecase:

function triggerSearchWhenFilterChangesSaga(action,state,dispatch) {
    var newState = searchFiltersReducer(action,state);
    var filtersHaveChanged =  (newState !== state);
    if ( filtersHaveChanged )  {
        triggerSearch(newFiltersState,dispatch)
    }
    return newState;
}


function searchFiltersReducer(action,state = {text: undefined,categories: []}) {
    switch (action.type) {
        case SEARCH_TEXT_CHANGED:
            return Object.assign({}, state, {text: action.text});
            break;
        case CATEGORY_SELECTED:
            return Object.assign({}, state, {categories: state.categories.concat(action.category) });
            break;
        case CATEGORY_UNSELECTED:
            return Object.assign({}, state, {categories: _.without(state.categories,action.category) });
            break;
    }
    return state;
}

Обратите внимание, что если вы используете какой-либо отвлекающий процесс (запись/воспроизведение/отмена/повторный/любой) отладчик, сага всегда должна быть отключена при повторном действии, потому что вы не хотите, чтобы новые действия отправлялись во время повтора.

EDIT: на языке Elm (из которого вдохновлен Redux) мы можем выполнять такие эффекты, "уменьшая" эффекты, а затем применяя их. См. Эту подпись: (state, action) -> (state, Effect)

В этом документе также есть длинное обсуждение.

ИЗМЕНИТЬ

Я раньше не знал, но в Redux action создатели могут получить доступ к состоянию. Поэтому большинство проблем, которые должна решить сага, часто могут решаться в создателях действия (но это создает более ненужную связь с состоянием пользовательского интерфейса):

function selectCategory(category) {
  return (dispatch, getState) => {
    dispatch({type: "CategorySelected",payload: category});
    dispatch({type: "SearchTriggered",payload: getState().filters});
  }
}