Как повторно использовать создателей экшн в Reaction-Redux? - PullRequest
0 голосов
/ 06 мая 2018

Мой вопрос о том, как структурировать редукторы и создателей действий, чтобы правильно их использовать. Я прочитал тонны библиографии в Интернете о составе редукторов и редукторах более высокого порядка и сумел сделать некоторые шаги в правильном направлении, создав фабрику / генератор редукторов с пространством имен. Благодаря этому у меня могут быть разные экземпляры одного и того же компонента / представления с независимыми состояниями, которые имеют общее поведение. Однако это не относится к компонентам / представлениям, которые имеют общие основания, но не равны. Скажем ... показать и отредактировать представление для объекта.

При монтировании оба эти компонента должны извлекать данные сущности из API одинаковым образом, однако компонент show обладает гораздо меньшими возможностями, чем компонент редактирования, который также обрабатывает отправку формы, обрабатывает ошибки и т. Д ...

Итак, сказав, что ... как я предполагаю расширить создателей действий editEntityReducer и editEntity, чтобы включить в них создателей действия entityReducer и entity, а также редактировать собственные функции редуктора и создателей действий?

Это то, что я имею до сих пор, используя в качестве примера сущность User:

Пользовательский редуктор + создатели действий (user.js):

import normalize from 'jsonapi-normalizer'
import { api, authenticatedHeaders } from 'api'
import { RSAA } from 'redux-api-middleware'
import { List, Record } from 'immutable'
import * as constants from './constants'

// ------------------------------------
// Actions
// ------------------------------------
export const destroyUser = (userId) => {
  // Uses redux-api-middleware. see: https://github.com/agraboso/redux-api-middleware
  return {
    [RSAA]: {
      endpoint: api.users.destroy.path(userId),
      method: api.users.destroy.method,
      headers: (state) => authenticatedHeaders(state.session.authorization.token),
      types: [
      constants.DESTROY_START,
      constants.DESTROY_SUCCESS,
      constants.DESTROY_FAIL]
    }
  }
}

export const fetchUser = (userId) => {
  // Uses redux-api-middleware. see: https://github.com/agraboso/redux-api-middleware
  return {
    [RSAA]: {
      endpoint: api.users.show.path(userId),
      method: api.users.show.method,
      headers: (state) => authenticatedHeaders(state.session.authorization.token),
      types: [
      constants.FETCH_START,
      {
        type: constants.FETCH_SUCCESS,
        payload: (action, state, res) => {
          return res.json().then(json => normalize(json))
        }
      },
      constants.FETCH_FAIL]
    }
  }
}

// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = (prefix) => {
  return {
    [`${prefix}_${constants.DESTROY_START}`]: (state, action) => {
      return state.set('destroying', true)
    },

    [`${prefix}_${constants.DESTROY_SUCCESS}`]: (state, action) => {
      return state.set('destroying', false)
    },

    [`${prefix}_${constants.DESTROY_FAIL}`]: (state, action) => {
      return state.set('destroying', false)
    },

    [`${prefix}_${constants.FETCH_START}`]: (state, action) => {
      return state.set('loading', true)
    },

    [`${prefix}_${constants.FETCH_SUCCESS}`]: (state, { payload }) => {
      const users = payload.entities.user
      const userIds = payload.result.user
      const roles = payload.entities.role

      // It's a single record fetch
      const user = users[userIds[0]]

      return state.merge({
        loading: false,
        record: Record({ user: Record(user)(), roles: Record(roles)() })()
      })
    },

    [`${prefix}_${constants.FETCH_FAIL}`]: (state, action) => {
      return state.set('loading', false)
    }
  }

}

// ------------------------------------
// Reducer
// ------------------------------------
const initialState = Record({
  destroying: false,
  loading: true, // initially true so will only go to false upong user loaded
  record: Record({ user: Record({})(), roles: List([]) })()
})()

const userReducer = (prefix = 'USER') => {
  if (prefix === undefined || prefix.length < 1) {
    throw new Error('prefix must be defined')
  }

  return (state = initialState, action) => {
    const handler = ACTION_HANDLERS(prefix)[`${prefix}_${action.type}`]
    return handler ? handler(state, action) : state
  }
}

export default userReducer

Редактирование пользовательского редуктора и создателей действий (edit_user.js):

import normalize from 'jsonapi-normalizer'
import { api, authenticatedHeaders } from 'api'
import { RSAA } from 'redux-api-middleware'
import { List, Record } from 'immutable'
import * as constants from './constants'

// ------------------------------------
// Actions
// ------------------------------------
export const updateUser = (userId, params = {}) => {
  // Uses redux-api-middleware. see: https://github.com/agraboso/redux-api-middleware
  return {
    [RSAA]: {
      endpoint: api.users.update.path(userId),
      method: api.users.update.method,
      headers: (state) => authenticatedHeaders(state.session.authorization.token),
      types: [
      constants.USER_UPDATE_START,
      constants.USER_UPDATE_SUCCESS,
      constants.USER_UPDATE_FAIL]
    }
  }
}

// TODO: see how to reuse this from the user.js file!
export const fetchUser = (userId) => {
  // Uses redux-api-middleware. see: https://github.com/agraboso/redux-api-middleware
  return {
    [RSAA]: {
      endpoint: api.users.show.path(userId),
      method: api.users.show.method,
      headers: (state) => authenticatedHeaders(state.session.authorization.token),
      types: [
      constants.USER_FETCH_START,
      {
        type: constants.USER_FETCH_SUCCESS,
        payload: (action, state, res) => {
          return res.json().then(json => normalize(json))
        }
      },
      constants.USER_FETCH_FAIL]
    }
  }
}

// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
  [constants.USER_UPDATE_START]: (state, action) => {
    return state.set('loading', true)
  },

  [constants.USER_UPDATE_SUCCESS]: (state, action) => {
    return state.set('loading', false)
  },

  [constants.USER_UPDATE_FAIL]: (state, action) => {
    return state.set('loading', false)
  },

  // TODO: this reducers are the same as user.js, reuse them!!
  [constants.USER_FETCH_START]: (state, action) => {
    return state.set('loading', true)
  },

  [constants.USER_FETCH_SUCCESS]: (state, { payload }) => {
    const users = payload.entities.user
    const userIds = payload.result.user
    const roles = payload.entities.role

    // It's a single record fetch
    const user = users[userIds[0]]

    return state.merge({
      loading: false,
      record: Record({ user: Record(user)(), roles: Record(roles)() })()
    })
  },

  [constants.USER_FETCH_FAIL]: (state, action) => {
    return state.set('loading', false)
  }

}

// ------------------------------------
// Reducer
// ------------------------------------
const initialState = Record({
  loading: true, // initially true so will only go to false upong user loaded
  record: Record({ user: Record({})(), roles: List([]) })()
})()

export default function editUserReducer (state = initialState, action) {
  const handler = ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}

Как вы можете видеть в коде TODO в коде, я хочу иметь возможность повторно использовать эти части редуктора и создателя действия, поскольку он может быть использован не только для базовых операций сущности, но и для любой общей операции CRUD на любом ресурсе, который мой приложение может использовать!

Спасибо

1 Ответ

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

Вы можете создать новую функцию и с различиями (из того, что я вижу, константы) извлекать в параметры либо в виде функции более высокого порядка (как я это делал ниже), либо комбинировать их с существующим параметром ( userId):

export const createFetchUser = (fetchStart, fetchSuccess, fetchFail) => userId =>
  // Uses redux-api-middleware. see: https://github.com/agraboso/redux-api-middleware
  ({
    [RSAA]: {
      endpoint: api.users.show.path(userId),
      method: api.users.show.method,
      headers: state => authenticatedHeaders(state.session.authorization.token),
      types: [
        fetchStart,
        {
          type: fetchSuccess,
          payload: (action, state, res) => res.json().then(json => normalize(json)),
        },
        fetchFail,
      ],
    },
  });

Затем вы можете импортировать эту функцию в ваши user.js и edit_user.js, чтобы создать функцию fetchUser для различных констант, например. для user.js:

export const fetchUser = userId =>
  createFetchUser(constants.FETCH_START, constants.FETCH_SUCCESS, constants.FETCH_FAIL);

Вы можете сделать нечто подобное для ваших редукторов.

...