Как избежать повторяющегося кода в Redux (подход уток)? - PullRequest
0 голосов
/ 18 мая 2018

Я работаю с React и Redux около 3 лет.Также я использую redux-thunk для асинхронных вещей.

И я их очень люблю, но недавно я заметил, что почти все утки в моем проекте используют одинаковую структуру действий, редукторов, селекторов и т. Д.

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

Код ниже покажет проблему более четко:

// ACTIONS

const const setUser = user => ({
  type: types.SET_USER,
  payload: user,
});

const cleanUser = () => ({ type: types.CLEAN_USER });

const fetchUser = userId => dispatch =>
  dispatch(fetchApi(userRequests.get(userId)))
    .then(response => dispatch(setUser(response)))
    .catch(error => showNotification(error));

// delete, update, etc... user actions

// REDUCER

const userReducer = (state = null, action) => {
  switch (action.type) {
    case types.SET_GROUP_ITEM:
      return action.payload;
    case types.CLEAN_GROUP_ITEM:
      return null;
    default:
      return state;
  }
};

Код выше показывает структуруuser из users duck, который будет почти таким же для других уток.

Есть ли способы уменьшить количество повторяющегося кода?Спасибо за продвижение!

1 Ответ

0 голосов
/ 19 мая 2018

Я заметил, что почти все утки в моем проекте используют одинаковую структуру действий, редукторов, селекторов и т. Д.

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

Например, мне всегда казалось, что меня волнует:

  1. Мы в данный момент выбираем объект?isFetching
  2. Были ли какие-либо ошибки при получении объекта?error
  3. Каковы фактические данные сущности?data
  4. Когда был последний раз выбран объект?lastUpdated

Кроме того, доменные объекты постоянно добавляются, поэтому непрерывное копирование и вставка редуктора / действий не идеальны.Нам нужен способ динамически хранения данных в Redux, и мы хотим, чтобы данные всегда были присоединены к таким свойствам, как isFetching и lastUpdated.

{
  "entities": {
    <SOME_ENTITY>: {
      "isFetching" : null    // Am I fetching?
      "lastUpdated": null    // When was I last fetched?
      "data"       : null    // Here's my data!
      "error"      : null    // Error during fetching
    }
  }
}

Так что, если мы сгенерировали действие со строковым литералом, который будет использоваться в качестве ключа в Redux (например, products, orders)?Таким образом, мы можем выдавать любые доступные типы действий (FETCH_REQUEST и т. Д.), И нам просто нужно обновить ключ entity, который автоматически выделит нам пространство в Магазине:

dispatch({
    entity     : "products",
    type       : "FETCH_SUCCESS", 
    data       : [{id: 1}],
    lastUpdated: Date.now()
});

dispatch({
    entity    : "orders",
    type      : "FETCH_SUCCESS",
    data      : [{id: 2}, {id: 3}],
    lastUpdated: Date.now()
});

Результирующее состояние

{
  "entities": {
    "products": {
      "isFetching" : false,
      "lastUpdated": 1526746314736,
      "data"       : [{id: 1}]
      "error"      : null
    },
    "orders": {
      "isFetching" : false,
      "lastUpdated": 1526746314943,
      "data"       : [{id: 2}, {id: 3}]
      "error"      : null
    }
  }
}

Универсальный редуктор сущностей

function entities (state = {}, action) {
    switch (action.type) {
        case FETCH_SUCCESS: // fall through
        case FETCH_FAILURE: // fall through
        case FETCH_REQUEST: {
            return Object.assign({}, state, {
                [action.entity]: entity(
                    state[action.entity],
                    action
                )
            });
        }
        default: {
            return state;
        }
    }
};

Редуктор сущностей

const INITIAL_ENTITY_STATE = {
    isFetching : false,
    lastUpdated: null,
    data       : null,
    error      : null
};

function entity (state = INITIAL_ENTITY_STATE, action) {
    switch (action.type) {
        case FETCH_REQUEST: {
            return Object.assign({}, state, {
                isFetching: true,
                error     : null
            });
        }
        case FETCH_SUCCESS: {
            return Object.assign({}, state, {
                isFetching : false,
                lastUpdated: action.lastUpdated,
                data       : action.data,
                error      : null
            });
        }
        case FETCH_FAILURE: {
            return Object.assign({}, state, {
                isFetching : false,
                lastUpdated: action.lastUpdated,
                data       : null,
                error      : action.error
            });
        }
    }
}

Опять же, используя универсальный редуктор, мы можем динамически сохранять все, что захотим, в Redux, поскольку мы используем строку entity ниже в качестве ключа в Redux

dispatch({type: "FETCH_REQUEST", entity: "foo"});
dispatch({type: "FETCH_REQUEST", entity: "bar"});
dispatch({type: "FETCH_REQUEST", entity: "baz"});

Результирующее состояние

{
  "entities": {
    "foo": {
      "isFetching": true,
      "error": null,
      "lastUpdated": null,
      "data": null
    },
    "bar": {
      "isFetching": true,
      "error": null,
      "lastUpdated": null,
      "data": null
    },
    "baz": {
      "isFetching": false,
      "error": null,
      "lastUpdated": null,
      "data": null
    }
  }
}

Если это выглядит интересно, я написал небольшую библиотеку (плагин!), Которая в точности соответствует описанному выше:

Демонстрация в реальном времени: http://mikechabot.github.io/react-boilerplate/dist/

Тем не менее, я не выдвигаю эту библиотеку, я простопытаясь описать подход, который я выбрал, учитывая мою проблему.Ваш набор действий может быть совершенно другим, и в этом случае вы все еще можете реализовать общий шаблон, но очевидно, что редуктор будет вести себя по-другому.

...