Как передать преобразователь или функцию обратного вызова в действие redux. Сериализация функций в хранилище redux для модальных окон и всплывающих уведомлений о подтверждении

При использовании универсального модального окна или всплывающего окна с кнопкой подтверждения становится полезной возможность передать действие в этот компонент, чтобы его можно было отправить, когда вы нажмете «Подтвердить».

Действие может выглядеть примерно так:

export function showConfirm({modalConfirm}) {
  return {
    type: 'MODALS/SHOW_MODAL',
    payload: {
      modalId: getUuid(),
      modalType: 'CONFIRM',
      modalConfirm : modalConfirm,
    },
  };
}

Где modalConfirm - другой объект действия, например:

const modalConfirm = {
  type: 'MAKE_SOME_CHANGES_AFTER_CONFIRM',
  payload: {}
}

Действие modalConfirm отправляется внутри модального компонента с использованием dispatch(modalConfirm) или даже dispatch(Object.assign({}, modalConfirm, someResultFromTheModal)

К сожалению, это решение работает, только если modalConfirm является простым объектом действия redux. Эта система явно очень ограничена. Есть ли способ передать функцию (например, преобразователь) вместо простого объекта?

В идеале, что-то полнофункциональное, например:

    const modalConfirm = (someResultFromTheModal) => {
      return (dispatch, getState){
        dispatch({
          type: 'MAKE_SOME_UPDATES',
          payload: someResultFromTheModal
        })
        dispatch({
          type: 'SAVE_SOME_STUFF',
          payload: http({
            method: 'POST',
            url: 'api/v1/save',
            data: getState().stuffToSave
          })
        })
      }
    }

person MrBlenny    schedule 21.09.2016    source источник


Ответы (2)


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

Ответ на ваш вопрос: «Да, но ....». Согласно часто задаваемым вопросам Redux на http://redux.js.org/docs/FAQ.html#organizing-state-non-serializable, вполне возможно поместить несериализуемые значения, такие как функции, в ваши действия и хранилище. Однако это обычно приводит к тому, что отладка путешествия во времени не работает должным образом. Если вас это не беспокоит, то вперед.

Другой вариант - разбить модальное подтверждение на две части. Пусть исходное модальное подтверждение по-прежнему будет простым объектом действия, но используйте промежуточное программное обеспечение, чтобы отслеживать его отправку и выполнять дополнительную работу оттуда. Это хороший вариант использования Redux-Saga.

person markerikson    schedule 21.09.2016

В итоге я использовал псевдонимы строк для библиотеки действий, которая централизованно регистрирует действия.

Действие модального эмиттера содержит объект с functionAlias и functionInputs

export function confirmDeleteProject({projectId}) {
  return ModalActions.showConfirm({
    message: 'Deleting a project it permanent. You will not be able to undo this.',
    modalConfirm: {
      functionAlias: 'ProjectActions.deleteProject',
      functionInputs: { projectId }
    }
  })
}

Где ProjectActions.deleteProject - это псевдоним для любого типа сложного действия, такого как:

export function deleteProject({projectId}) {
  return (dispatch)=>{
    dispatch({
      type: 'PROJECTS/DELETE_PROJECT',
      payload: http({
        method: 'DELETE',
        url: `http://localhost:3000/api/v1/projects/${projectId}`,
      }).then((response)=>{
        dispatch(push(`/`))
      }),
      meta: {
        projectId
      }
    });
  }
}

Функции регистрируются в библиотечном модуле следующим образом:

import * as ProjectActions from '../../actions/projects.js';

const library = {
  ProjectActions: ProjectActions,
}

export const addModule = (moduleName, functions) => {
  library[moduleName] = functions
}

export const getFunction = (path) => {
  const [moduleName, functionName] = path.split('.');

  // We are getting the module only
  if(!functionName){
    if(library[moduleName]){
      return library[moduleName]
    }
    else{
      console.error(`Module: ${moduleName} could not be found.`);
    }
  }
  // We are getting a function
  else{
    if(library[moduleName] && library[moduleName][functionName]){
      return library[moduleName][functionName]
    }
    else{
      console.error(`Function: ${moduleName}.${functionName} could not be found.`);
    }
  }
}

Объект modalConfirm передается в модальный объект через props. Модальному компоненту требуется функция getFunction в модуле выше. Объект modalConfirm преобразуется в функцию следующим образом:

const modalConfirmFunction = (extendObject, modalConfirm) => {
  const functionFromAlias = getFunction(modalConfirm.functionAlias);
  if(functionFromAlias){
    dispatch(functionFromAlias(Object.assign({}, modalConfirm.functionInputs, extendObject)));
  }
}

Как видите, эта функция может принимать входные данные из модального окна. Он может выполнять любое сложное действие или преобразователь. Эта система не мешает путешествию во времени, но централизованная библиотека является небольшим недостатком.

person MrBlenny    schedule 23.09.2016
comment
Вы по-прежнему используете этот подход с момента получения ответа или перешли на что-то другое? - person Neovea; 28.07.2017
comment
Да, это единственная система, которую я смог придумать, которая может выдерживать путешествия во времени / перезапуски приложений или работать в многопоточном электронном приложении. Тем не менее, при работе в однопоточной среде, такой как веб-сайт, я просто использую кеш обещаний. Это гораздо более удобная система для работы, но не такая надежная ... Вы можете понять это здесь: github.com/MrBlenny/react-redux-promise-modals/tree/master/src Этот пример специфичен для модальных окон, но его можно сделать общим. Идентификатор обещания передается внутри действия, а затем это обещание разрешается / отклоняется. - person MrBlenny; 30.07.2017