Code Monkey home page Code Monkey logo

redux-logic's Introduction

redux-logic

"One place for all your business logic and action side effects"

Redux middleware that can:

  • intercept (validate/transform/augment) actions AND
  • perform async processing (fetching, I/O, side effects)

Build Status Known Vulnerabilities NPM Version Badge

tl;dr

With redux-logic, you have the freedom to write your logic in your favorite JS style:

  • plain callback code - dispatch(resultAction)
  • promises - return axios.get(url).then(...)
  • async/await - result = await fetch(url)
  • observables - ob$.next(action1)

Use the type of code you and your team are comfortable and experienced with.

Leverage powerful declarative features by simply setting properties:

  • filtering for action type(s) or with regular expression(s)
  • cancellation on receiving action type(s)
  • use only response for the latest request
  • debouncing\
  • throttling
  • dispatch actions - auto decoration of payloads

Testing your logic is straight forward and simple. redux-logic-test provides additional utilities to make testing a breeze.

With simple code your logic can:

  • intercept actions before they hit the reducer
    • validate, verify, auth check actions and allow/reject or modify actions
    • transform - augment/enhance/modify actions
  • process - async processing and dispatching, orchestration, I/O (ajax, REST, subscriptions, GraphQL, web sockets, ...)

Redux-logic makes it easy to use code that is split into bundles, so you can dynamically load logic right along with your split UI.

Server rendering is simplified with redux-logic since it lets you know when all your async fetching is complete without manual tracking.

Inspired by redux-observable epics, redux-saga, and custom redux middleware, redux-logic combines ideas of each into a simple easy to use API.

Quick Example

This is an example of logic which will listen for actions of type FETCH_POLLS and it will perform ajax request to fetch data for which it dispatches the results (or error) on completion. It supports cancellation by allowing anything to send an action of type CANCEL_FETCH_POLLS. It also uses take latest feature that if additional FETCH_POLLS actions come in before this completes, it will ignore the outdated requests.

The developer can just declare the type filtering, cancellation, and take latest behavior, no code needs to be written for that. That leaves the developer to focus on the real business requirements which are invoked in the process hook.

import { createLogic } from 'redux-logic';

const fetchPollsLogic = createLogic({
  // declarative built-in functionality wraps your code
  type: FETCH_POLLS, // only apply this logic to this type
  cancelType: CANCEL_FETCH_POLLS, // cancel on this type
  latest: true, // only take latest

  // your code here, hook into one or more of these execution
  // phases: validate, transform, and/or process
  process({ getState, action }, dispatch, done) {
    axios
      .get('https://survey.codewinds.com/polls')
      .then((resp) => resp.data.polls)
      .then((polls) => dispatch({ type: FETCH_POLLS_SUCCESS, payload: polls }))
      .catch((err) => {
        console.error(err); // log since could be render err
        dispatch({ type: FETCH_POLLS_FAILED, payload: err, error: true });
      })
      .then(() => done()); // call done when finished dispatching
  }
});

Since redux-logic gives you the freedom to use your favorite style of JS code (callbacks, promises, async/await, observables), it supports many features to make that easier, explained in more detail

Table of contents

Updates

Full release notes of breaking and notable changes are available in releases. This project follows semantic versioning.

A few recent changes that are noteworthy:

v2.0.0

Updated to RxJS@6. Your logic code can continue to use RxJS@5 until you are ready to upgrade to 6.

Optimizations to reduce the stack used, especially if a subset of features is used.

v1.0.0

Transpilation switched to Babel 7 and Webpack 4

v0.12

These changes are not breaking but they are noteworthy since they prepare for the next version which will be breaking mainly to remove the single dispatch version of process hook which has been a source of confusion.

  • Single dispatch signature for process hook is deprecated and warns in development build. This is when you use the signature process(deps, dispatch) (including dispatch but not done). To migrate change your use to include done process(deps, dispatch, done) and call the done cb when done dispatching.
  • New option warnTimeout defaults to 60000 (ms == one minute) which warns (in development build only) when the logic exceeds the specified time without completion. Adjust this value or set it to 0 if you have logic that needs to exceed this time or purposefully never ends (like listening to a web socket)

Goals

  • organize business logic keeping action creators and reducers clean
    • action creators are light and just post action objects
    • reducers just focus on updating state
    • intercept and perform validations, verifications, authentication
    • intercept and transform actions
    • perform async processing, orchestration, dispatch actions
  • wrap your core business logic code with declarative behavior
    • filtered - apply to one or many action types or even all actions
    • cancellable - async work can be cancelled
    • limiting (like taking only the latest, throttling, and debouncing)
  • features to support business logic and large apps
    • have access to full state to make decisions
    • easily composable to support large applications
    • inject dependencies into your logic, so you have everything needed in your logic code
    • dynamic loading of logic for splitting bundles in your app
    • your core logic code stays focussed and simple, don't use generators or observables unless you want to.
    • create subscriptions - streaming updates
    • easy testing - since your code is just a function it's easy to isolate and test

Usage

redux-logic uses rxjs@6 under the covers and to prevent multiple copies (of different versions) from being installed, it is recommended to install rxjs first before redux-logic. That way you can use the same copy of rxjs elsewhere.

If you are never using rxjs outside of redux-logic and don't plan to use Observables directly in your logic then you can skip the rxjs install and it will be installed as a redux-logic dependency. However if you think you might use Observables directly in the future (possibly creating Observables in your logic), it is still recommended to install rxjs separately first just to help ensure that only one copy will be in the project.

The rxjs install below npm install rxjs@^6 installs the lastest 6.x.x version of rxjs.

npm install rxjs@^6 --save  # optional see note above
npm install redux-logic --save
// in configureStore.js
import { createLogicMiddleware } from 'redux-logic';
import rootReducer from './rootReducer';
import arrLogic from './logic';

const deps = { // optional injected dependencies for logic
  // anything you need to have available in your logic
  A_SECRET_KEY: 'dsfjsdkfjsdlfjls',
  firebase: firebaseInstance
};

const logicMiddleware = createLogicMiddleware(arrLogic, deps);

const middleware = applyMiddleware(
  logicMiddleware
);

const enhancer = middleware; // could compose in dev tools too

export default function configureStore() {
  const store = createStore(rootReducer, enhancer);
  return store;
}


// in logic.js - combines logic from across many files, just
// a simple array of logic to be used for this app
export default [
 ...todoLogic,
 ...pollsLogic
];


// in polls/logic.js
import { createLogic } from 'redux-logic';

const validationLogic = createLogic({
  type: ADD_USER,
  validate({ getState, action }, allow, reject) {
    const user = action.payload;
    if (!getState().users[user.id]) { // can also hit server to check
      allow(action);
    } else {
      reject({ type: USER_EXISTS_ERROR, payload: user, error: true })
    }
  }
});

const addUniqueId = createLogic({
  type: '*',
  transform({ getState, action }, next) {
    // add unique tid to action.meta of every action
    const existingMeta = action.meta || {};
    const meta = {
      ...existingMeta,
      tid: shortid.generate()
    },
    next({
      ...action,
      meta
    });
  }
});

const fetchPollsLogic = createLogic({
  type: FETCH_POLLS, // only apply this logic to this type
  cancelType: CANCEL_FETCH_POLLS, // cancel on this type
  latest: true, // only take latest
  process({ getState, action }, dispatch, done) {
    axios.get('https://survey.codewinds.com/polls')
      .then(resp => resp.data.polls)
      .then(polls => dispatch({ type: FETCH_POLLS_SUCCESS,
                                payload: polls }))
      .catch(err => {
             console.error(err); // log since could be render err
             dispatch({ type: FETCH_POLLS_FAILED, payload: err,
                        error: true })
      })
      .then(() => done());
  }
});

// pollsLogic
export default [
  validationLogic,
  addUniqueId,
  fetchPollsLogic
];

processOptions introduced for [email protected] allowing for even more streamlined code

processOptions has these new properties which affect the process hook behavior:

  • dispatchReturn - the returned value of the process function will be dispatched or if it is a promise or observable then the resolve, reject, or observable values will be dispatched applying any successType or failType logic if defined. Default is determined by arity of process fn, true if dispatch not provided, false otherwise. Details

  • successType - dispatch this action type using contents of dispatch as the payload (also would work with with promise or observable). You may alternatively provide an action creator function to use instead and it will receive the value as only parameter. Default: undefined.

    • if successType is a string action type

      • create action using successType and provide value as payload. ex: with successType:'FOO', result would be { type: 'FOO', payload: value }
    • if successType is an action creator fn receiving the value as only parameter

      • use the return value from the action creator fn for dispatching ex: successType: x => ({ type: 'FOO', payload: x })
      • if the action creator fn returns a falsey value like undefined then nothing will be dispatched. This allows your action creator to control whether something is actually dispatched based on the value provided to it.
  • failType - dispatch this action type using contents of error as the payload, sets error: true (would also work for rejects of promises or error from observable). You may alternatively provide an action creator function to use instead which will receive the error as the only parameter. Default: undefined.

    • if failType is a string action type

      • create action using failType, provide value as the payload, and set error to true. ex: with failType:'BAR', result would be { type: 'BAR', payload: errorValue, error: true }
    • if failType is an action creator function receiving the error value as its only parameter

      • use the return value from the action creator fn for dispatching. ex: failType: x => ({ type: 'BAR', payload: x, error: true })
      • if the action creator fn returns a falsey value like undefined then nothing will be dispatched. This allows your action creator to control whether something is actually dispatched based on the value provided to it.

The successType and failType would enable clean code, where you can simply return a promise or observable that resolves to the payload and rejects on error. The resulting code doesn't have to deal with dispatch and actions directly.

import { createLogic } from 'redux-logic';

const fetchPollsLogic = createLogic({
  // declarative built-in functionality wraps your code
  type: FETCH_POLLS, // only apply this logic to this type
  cancelType: CANCEL_FETCH_POLLS, // cancel on this type
  latest: true, // only take latest

  processOptions: {
    // optional since the default is true when dispatch is omitted from
    // the process fn signature
    dispatchReturn: true, // use returned/resolved value(s) for dispatching
    // provide action types or action creator functions to be used
    // with the resolved/rejected values from promise/observable returned
    successType: FETCH_POLLS_SUCCESS, // dispatch this success act type
    failType: FETCH_POLLS_FAILED // dispatch this failed action type
  },

  // Omitting dispatch from the signature below makes the default for
  // dispatchReturn true allowing you to simply return obj, promise, obs
  // not needing to use dispatch directly
  process({ getState, action }) {
    return axios.get('https://survey.codewinds.com/polls').then((resp) => resp.data.polls);
  }
});

This is pretty nice leaving us with mainly our business logic code that could be easily extracted and called from here.

Full API

See the docs for the full api

Examples

Live examples

Full examples

https://github.com/jeffbski/redux-logic-examples/tree/master/examples/search-async-fetch

Comparison summaries

Following are just short summaries to compare redux-logic to other approaches.

For a more detailed comparison with examples, see by article in docs, Where do I put my business logic in a React-Redux application?.

Compared to fat action creators

  • no easy way to cancel or do limiting like take latest with fat action creators
  • action creators would not have access to the full global state so you might have to pass down lots of extra data that isn't needed for rendering. Every time business logic changes might require new data to be made available
  • no global interception using just action creators - applying logic or transformations across all or many actions
  • Testing components and fat action creators may require running the code (possibly mocked API calls).

Compared to redux-thunk

  • With thunks business logic is spread over action creators
  • With thunks there is not an easy way to cancel async work nor to perform (take latest) limiting
  • no global interception with thunks - applying logic or transformations across all or many actions
  • Testing components and thunked action creators may require running the code (possibly mocked API calls). When you have a thunk (function or promise) you don't know what it does unless you execute it.

Compared to redux-observable

  • redux-logic doesn't require the developer to use rxjs observables. It uses observables under the covers to provide cancellation, throttling, etc. You simply configure these parameters to get this functionality. You can still use rxjs in your code if you want, but not a requirement.
  • redux-logic hooks in before the reducer stack like middleware allowing validation, verification, auth, transformations. Allow, reject, transform actions before they hit your reducers to update your state as well as accessing state after reducers have run. redux-observable hooks in after the reducers have updated state so they have no opportunity to prevent the updates.

Compared to redux-saga

  • redux-logic doesn't require you to code with generators
  • redux-saga relies on pulling data (usually in a never ending loop) while redux-logic and logic are reactive, responding to data as it is available
  • redux-saga runs after reducers have been run, redux-logic can intercept and allow/reject/modify before reducers run also as well as after

Compared to custom redux middleware

  • Both are fully featured to do any type of business logic (validations, transformations, processing)
  • redux-logic already has built-in capabilities for some of the hard stuff like cancellation, limiting, dynamic loading of code. With custom middleware you have to implement all functionality.
  • No safety net, if things break it could stop all of your future actions
  • Testing requires some mocking or setup

Implementing SAM/PAL Pattern

The SAM (State-Action-Model) pattern is a pattern introduced by Jean-Jacques Dubray. Also known as the PAL (proposer, acceptor, learner) pattern based on Paxos terminology.

A few of the challenging parts of implementing this with a React-Redux application are:

  1. where to perform the accept (interception) of the proposed action performing validation, verification, authentication against the current model state. Based on the current state, it might be appropriate to modify the action, dispatch a different action, or simply suppress the action.
  2. how to trigger actions based on the state after the model has finished updating, referred to as the NAP (next-action-predicate).

Custom Redux middleware can be introduced to perform this logic, but you'll be implementing most everything on your own.

With redux-logic you can implement the SAM / PAL pattern easily in your React/Redux apps.

Namely you can separate out your business logic from your action creators and reducers keeping them thin. redux-logic provides a nice place to accept, reject, and transform actions before your reducers are run. You have access to the full state to make decisions and you can trigger actions based on the updated state as well.

Solving those SAM challenges previously identified using redux-logic:

  1. perform acceptance in redux-logic validate hooks, you have access to the full state (model) of the app to make decisions. You can perform synchronous or asynchronous logic to determine whether to accept the action and you may augment, modify, substitute actions, or suppress as desired.
  2. Perform NAP processing in redux-logic process hooks. The process hook runs after the actions have been sent down to the reducers so you have access to the full model (state) after the updates where you can make decisions and dispatch additional actions based on the updated state.

Inspiration

redux-logic was inspired from these projects:

Minimized/gzipped size with all deps

(redux-logic only includes the modules of RxJS 6 that it uses)

redux-logic.min.js.gz 18KB

Note: If you are already including RxJS 6 into your project then the resulting delta will be much smaller.

TODO

  • more docs
  • more examples

Get involved

If you have input or ideas or would like to get involved, you may:

Supporters

This project is supported by CodeWinds Training

redux-logic's People

Contributors

a-marquez avatar allenfang avatar alvis avatar bzbetty avatar cherniavskii avatar dependabot[bot] avatar dominicboettger avatar fueledbymarvin avatar gamtiq avatar ikorolev93 avatar jeffbski avatar jeremaiha avatar kevinold avatar lidoravitan avatar matthamil avatar proxiweb avatar riston avatar sanbornhilland avatar saurabhsood91 avatar skoob13 avatar skred avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

redux-logic's Issues

how to perform a race condition

  const winner = yield race({
    auth: call(getAuthorize, { username, password, isRegistering: false }),
    logout: take(LOGOUT),
  });

  // If `authorize` was the winner...
  if (winner.auth) {
    // ...we send Redux appropiate actions
    yield put(changeForm({ username: '', password: '' })); // Clear form
    forwardTo(pages.pageDashboard.path); // Go to dashboard page
    // If `logout` won...
  } else if (winner.logout) {
    // ...we send Redux appropiate action
    yield call(logout); // Call `logout` effect
    forwardTo(pages.pageLogin.path); // Go to root page
  }

Is it possible to produce a race like this saga ?

Request - example using web sockets.

Perhaps community would benefit from a small example using web sockets in addition to classic ajax requests. I'm clear on how to send messages to the server but not sure how to deal with messages arriving from the server.

Question: Catching Errors

I'm having trouble with errors being swallowed and never bubbling up. I can't seem to use the promise .catch(), or wrap in a try/catch.

Specifically, when I dispatch an action that results in a state change, React re-renders my components. If React throws an error during the render method I can't seem to get ahold of it.

I don't know much about RxJS/Observables... but I can see that my error is being caught by RxJS here https://github.com/ReactiveX/rxjs/blob/master/src/Subscriber.ts#L260-L265 and then I don't know where it goes. I'm assuming redux-logic should throw the error being passed back by RxJS, or use the failType or UNHANDLED_LOGIC_ERROR action types to someone notify me of this error.

As it stands now... it's just silent and everything stops. Any help, or an explanation would be helpful. What is the proper way to handle errors like this with redux-logic?

Example code:

export const someLogic = createLogic({
  type: ACTION_TYPES.LOGIC_EXAMPLE,
  process({ getState, action }, dispatch, done) {
    axios.get('/data')
      .then(res => {
        dispatch(setUser({ user: {name: 'crash render()'} }))
      }).catch(err => {
        // never gets called
        console.error(err)
      }).then(() => done())
  }
})

Thanks!

redux-logic + flow best practice?

Hi,

Currently I'm struggling with the use of Flow in combination with redux-logic because Flow does not understand that a given logic will only receive actions of a given type.

Given the following types:

export type Action =
          { type: 'SEARCH_LOCATION_SELECT', payload: string }
          | { type: 'GEOLOCATION_REQUEST' };

export type Dispatch = (action: Action) => any;

export type LogicContextT = {
  action: Action,
  getState: () => GlobalState
}

This is a problem for Flow:

const aroundMeSelectLogic = createLogic({
  type: 'SEARCH_LOCATION_SELECT',
  process({ action }: LogicContextT, dispatch: Dispatch): void {
    if (action.payload === 'AROUND_ME') {
      dispatch(geolocationRequest());
    }
  }
});

property payload. Property not found in object type

and this is NOT a problem for Flow:

const aroundMeSelectLogic = createLogic({
  type: '*',
  process({ action }: LogicContextT, dispatch: Dispatch): void {
    if (action.type === 'SEARCH_LOCATION_SELECT' && action.payload === 'AROUND_ME') {
      dispatch(geolocationRequest());
    }
  }
});

Does anyone have a suggestion on how to best handle this?

RFC - redux-logic testimonials - share your experience in a comment to encourage others to try redux-logic

In this competitive world of JS libraries, many people won't even look at something unless there are compelling testimonials by other users, so I'd like to add some of your comments to the redux-logic home page to encourage others to give it a try.

If you would be willing to leave a short 1-2 sentence testimonial comment along with your name and any title/company that you want included then I'll add those to the README. You can add those as a comments on this issue or if you would rather email me, that's fine too. My email is available off the github profile here

Thanks in advance!

How about reusability of logic?

For example, for a logic I created, can I reuse it without redux?

Maybe this is not a good question. Because I can always extract very common part into a separate library for reusing.

I am just wondering what do you think.

By the way, I like this library a lot. It has the potential to be come a popular one.

processOptions.failType and dispatch in promise doesn't work

I am trying to throw an error in my promise chain with the following code :

  processOptions: { // options configuring the process hook below
    // on error/reject, automatically dispatch(repoLoadingError(err))
    failType: requestError, // action creator which accepts error
  },

  // perform async processing, Using redux-logic promise features
  // so it will dispatch the resolved action from the returned promise.
  // on error, it dispatches the using the action creator set in
  // processOptions.failType above `repoLoadingError(err)`
  // requestUtil was injected in store.js createLogicMiddleware
  process({ getState, authService, action }, dispatch) {
    const { username, password } = action.data;
    dispatch(sendingRequest(true));

    let codeUrl;
    return authService.preLogin()
      .then((res) => authService.login(username, password))
      .then((jwt) => {
        if (jwt) { // force the error
          return Promise.reject(new Error('It appear there is a problem with your account, please contact an administrator.'));
        }
        dispatch(put(jwtLoaded(jwt)));
      });

The reject doesn't get caught :

logic.js?d876:74 Uncaught (in promise) Error: It appear there is a problem with your account, please contact an administrator

This is the content of a authService.login :

  login(username, password) {
    const options = {
      method: 'POST',
      headers: {
        'Accept': 'application/json', // eslint-disable-line quote-props
        'Content-Type': 'application/x-www-form-urlencoded',
      },
    };

    const queryString = toQueryString({ username, password });

    return request(`${url.login}?${queryString}`, options)
      .then((response) => Promise.resolve(response));
  },

I have the feeling that it is not possibme to dispatch within the promise.
Is it a bug or is it me ?

redux-logic login flow example?

Hi@all,
is there an example of how to implement the login flow using redux-logic and JWT? I'm very much interested in how to implement the refresh token logic to be specific. Could this be something that should be done in the validate function?

Thanks for any help

Comment: include `import` statement in all examples

Please include all import statement in examples. Dont assume we know where things are being imported from.

Also, just another point.. this is awesome.

Also, any chance you can make this lighter? RXjs is cool, but such a large dep. I believe this could be done far lighter. Just an intuition..

Custom actions equality function

Let's say a have a logic for a generic data fetching which allows for fetching from multiple endpoints. Fetching happens after a particular action is dispatched (like FETCH_DATA), but action has additional metadata, for example:

type: FETCH_DATA,
meta: {
  endpoint: '...',
  resourceID: '...',
}

I want to implement remote autocomplete (fetch more specified data as user types), so I thought of using latest, debounce etc. options with the logic. The problem is when I want to dispatch multiple FETCH_DATA for multiple resources (say users and orders) - logic only looks at the action type when debouncing etc. I think it may be useful to allow for (optional) custom function that would check for action equality. What do you think, @jeffbski?

Question: example on using redux-logic inside the react-boilerplate?

Is there and example showing how to use redux-logic inside the react-boilerplate?
The issue is that you cannot have a single rootLogic that imports all the application logic because this will prevent code splitting by route that is built into this boilerplate.

I need some help on how to replace the redux-saga with redux-logic and still maintain the code splitting intact.
Thank you

Take latest

Hi ,

I am checking the simple example http://jsfiddle.net/jeffbski/954g5n7h , and its using latest: true, that means it has to cancel previous requests as per documentation.

But I observed its not cancelling the previous requests, instead its waiting until all the previous requests are completed, is it correct way , am I understanding the feature wrongly ?

Cancellation using axios

The section on cancellation states

Many libraries like axios and fetch don't support aborting/cancelling of an in-flight request

However this is no-longer the case with axios which now supports cancellation tokens to cancel requests (see https://github.com/mzabriskie/axios#cancellation)

I am currently using the cancelType in my logic to prevent the results the being dispatched, but as you state in the documentation, this does not actually cancel the inflight request.

Is there a hook or anyway to pass a cancellation token to redux-logic so that when the cancelType is dispatched, cancel() is called on the cancellation token.

Feature Request: retryWhen policy

It would be great to have retryWhen policy as a configuration to createLogic. So, it will be easier to implement retry policy without digging into observables.
Ideally, just expose predicate function with some context information as a parameter to that function.

retryWhen(({retry, action, cancelled$}) => {
  // retry object may contain logic or retry specific options
  // and some methods to deal with retry policy
  retry.delay(retry.throttle);
  return retry.errorCount <=2;
})

RFC: redux-logic-test - helper utilities

I'm thinking it might be nice to create a separate helper project which will help make it easy to create mock stores for integration level tests. It would basically let you setup your test store in one line of code hooking up as much or as little as you want to specify.

Then it provides a few nice methods to facilitate testing like knowing when the logic is complete and then being able to see an array list of actions that were dispatched.

Let me know what you think. I want it to be simple but yet including the right things to make testing easy.

import { createMockStore } from 'redux-logic-test';

// specify as much as necessary for your particular test
const store = createMockStore({
  initialState: optionalObject,
  reducer: optionalFn, // default: identity reducer fn(state, action) { return state; }
  logic: optionalLogic, // default: [] - used for the logicMiddleware that is created
  injectedDeps: optionalObject, // default {} - used for the logicMiddleware that is created
  middleware: optionalArr // other mw, exclude logicMiddleware from this array
});
store.dispatch(...) // use as necessary for your test
store.whenComplete(fn) - shorthand for store.logicMiddleware.whenComplete(fn) - when all inflight logic has all completed calls fn and returns promise
store.actions - the actions dispatched, use store.resetActions() to clear
store.resetActions() - clear store.actions
store.logicMiddlware // access logicMiddleware automatically created from logic/injectedDeps props
// allows you to use store.logicMiddleware.addLogic, mergeNewLogic, replaceLogic, whenComplete, monitor$


// So a test might look something like

const store = createMockStore({
  initialState: { foo: 42 },
  logic: [ fooLogic, barLogic ], // logicMiddleware will be created for this logic and injectedDeps
  injectedDeps: { api: myAPI }
});

store.dispatch({ type: FOO });
store.dispatch({ type: BAR });
store.whenComplete(() => { // when logic is finished
  expect(store.actions).toEqual([ // check the actions that were dispatched
    { type: FOO },
    { type: BAR },
    { type: FOO_SUCCESS, payload: [] },
    { type: BAR_SUCCESS, payload: {} }
  ]);
});

I didn't require a reducer so I didn't need to create one, but if you test needs that you can supply one.

I have a gist of the API here

Question: Workflow

Do you find yourself creating constants in the following pattern?

SEARCH_FILTERS_LOAD_REQUEST

SEARCH_FILTERS_LOAD_SUCCESS

SEARCH_FILTERS_LOAD_FAILURE

The three constants above are associated with an asynchronous operation.

redux-logic handles the first constant by making an asynchronous call (I use async/await). My reducer also handles the first constant by updating some "loading" state variable which controls various loading indicators in the UI.

Upon success or failure, redux-logic dispatches one of the final two constants, which the reducer acts on (updating the search filters, setting the loading state to false, etc.), and perhaps dispatches an action to a toastr or modal store as well and then calls done().

Is this kind of three-pronged approach an anti-pattern?

Question: Handling UI side-effects

When using thunks, UI side-effects such as route rediection, toast notifications etc, can be handled by chaining onto the promise returned by the thunk

For example in a form submit handler

handleSubmit(values) {
 return createEvent(values)
    .then(() => routerActions.push('/'))
}

To accomplish the same using logic I'm having to intercept the creation success action and do the redirection there

For example

const createUnpublishedActivityFulfilledLogic = createLogic({
    type: createUnpublishedActivityFulfilled,

    process({ routerActions }) {
        return routerActions.push('/')
    },
})

It doesn't seem right to me to have UI concerns within logic. It now means that the create action is no-longer re-usable as it will always cause a redirection on success.

Is there another way to handle this scenario?

Question: parallel process() ?

https://jsfiddle.net/jeffbski/954g5n7h/

Why is it that in the above fiddle, if you click the 'fetch users' button 5 times it takes 10s to respond? Shouldn't the next event either cancel the previous one, or at least be done in parallel?

Even if you dispatch a 'USERS_FETCH_CANCEL' (by clicking 'cancel') between each 'USERS_FETCH' you still get this behaviour. The request is still in pending state, even after clicking cancel, and additional requests are waiting for previous ones.

Thanks!

Server side rendering implementation?

I'm getting cannot assign logicMiddleware instance to multiple stores, create separate instance for each when try to load it on server few times.

Check for and add error.message when auto-dispatching failTypes

Hi. I'm using the auto-dispatch method now in most all of my "logics". However, I have found that unless a Promise is explicitly rejected, the error message doesn't seem to be added to the resulting failType's payload, and no message is written to the console.

Would it be possible to check and add error.message to the payload if it exists?

Here is a sample that you can setup to try this out.

const testingLogic = createLogic({
  type: 'TESTING',
  latest: true,
  processOptions: {
    successType: 'TESTING_SUCCESS',
    failType: 'TESTING_FAILURE'
  },

  process({action}) {
    return new Promise((resolve, reject) => {
      if (action.payload === 'fail') {
        reject('Failed with reject');
      } else if (action.payload === 'pass') {
        resolve('passed');
      } else {
        const foo = action.get('foo'); // There is no `get` method on action
      }
    });
  }
});

Then, in Redux tools, dispatch the following:

{
  type: 'TESTING',
  payload: 'fail'
}

This will result in a nice action with an error message:

{
  type: 'TESTING_FAILURE',
  payload: 'Failed with reject',
  error: true
}

However, sending foo through as the payload like so:

{
  type: 'TESTING',
  payload: 'foo'
}

Results in a dispatched action with no error message, and of course, no console message either.

{
  type: 'TESTING_FAILURE',
  payload: {},
  error: true
}

Errors being swallowed in Reducer

Hey Jeff,

I'm seeing a similar issue to #46 again except this time it's when the error gets thrown in the reducer itself.

Here's a fiddle [redacted]
This is a copy of the working demo from #46 except this time the error is being thrown within the reducer itself (on line 68) instead of in render()

Getting dispatch to work on recursive calls.

Not sure if this is the correct place for this.
I am currently using redux logic and i'm trying to deal with a paginated API.
I am trying to pass dispatch to an auxiliary function that will attempt to pull all the pages of the resultset and concat them to the store.
I pass the dispatch to this auxiliary function and it works fine, but when i recurse on this same auxiliary function, to try to pull the next set of results and pass the dispatch in again, it doesn't actually dispatch any actions, and it doesn't throw any errors.
I have made sure multiple dispatches were turned on by dispatching the same action 3 times before the recursion started and that worked.

Any ideas why the dispatch wont work past the first auxiliary function call?

RFC: plan for deprecating process hook's single-dispatch mode over the next couple versions

I wanted to see if anyone has any thoughts about this. I'm thinking it would be best to deprecate the process hook's single-dispatch mode which comes into play when you use the signature process({ getState, action }, dispatch)

// single dispatch mode enabled by including dispatch but not done
process({ getState, action }, dispatch) {
  dispatch(newAction) // call dispatch exactly once
}

We would keep the other two process hook signatures:

  1. process({ getState, action }, dispatch, done) // multi-dispatch
  2. process({ getState, action }) // return dispatching

We would also deprecate the dispatchMultiple option and add a warnTimeout option.

Here's some background on the issue and my proposed plan for deprecating is at the end.

tl;dr

The process hook has a variable signature that enables three modes currently (multi-dispatch, single-dispatch, and return dispatching). The original mode single-dispatch is confusing and easily misused by users.

Deprecating and eventually eliminating single-dispatch mode should reduce confusion and prevent many mistakes.

A new warnTimeout (configurable by option) will inform users in development if they are failing to call done after finishing dispatching. It can be disabled for intentional never ending logic.

Backstory

In that single-dispatch mode, the process hook expects dispatch to be called exactly once.

This is problematic since newcomers don't realize the nature of this, so they copy this single-dispatch code and will try to use it for multiple dispatches accidentally. It isn't obvious from the signature that they can't do this.

Another problem with single-dispatch mode is that since it requires dispatch to be called exactly once, you have to call it empty if you don't want to dispatch anything dispatch() which is also counter intuitive.

So while it seemed like a good idea originally for a common use case, it ends up being more confusing for people and that's not good.

However switching to the full signature (multiple-dispatch) form of process solves all of these problems with the slight disadvantage of an extra line of code to call done.

// normal multiple-dispatch mode
process({ getState, action }, dispatch, done) {
  dispatch(newAction) // call dispatch zero to many times
  done(); // call done when finished dispatching
}

To avoid confusion, I've changed the examples to use this since it is safer for people to learn from and they can do 0 to many dispatches as they see fit.

So going forward it is my proposal that we support only two of the three signatures for the process hook, dropping the single-dispatch mode.

  1. process({ getState, action }, dispatch, done) // normal multi-dispach
  2. process({ getState, action }) // return dispatching

For those that haven't seen the second form which was introduced with processOptions is great for working with promises, async/await, and observables. It uses the return value for dispatching:

  • undefined - no dispatch
  • object - assumed to be an action, so dispatched
  • promise - dispatch the resolved/rejected actions
  • observable - dispatch the next/error actions
  • error - dispatch the error, wrapping if no type

You can also influence the dispatching with processOptions as mentioned in the docs.

Proposed plan for deprecating process hook's single-dispatch mode

deprecate single-dispatch mode - breaking change v0.12

  1. add warnTimeout option w/default of 60s - (development ENV only) that warns in console.error if logic is still running after the elapsed time
    • logic is still running after 60s, forget to call done()? For non-ending logic, set warnTimeout: 0.
  2. console.error in createLogic for process hook definitions that omit done and don't have either dispatchMultiple=true or warnTimeout=0 (!production ENV) that
    • single-dispatch mode is deprecated, call done when finished dispatching. For non-ending logic, set warnTimeout: 0
  3. console.error in createLogic for processOptions.dispatchMultiple=true (!production ENV),
    • dispatchMultiple is always true in next version. For non-ending logic, set warnTimeout to 0

eliminate single-dispatch mode - breaking change v0.13

(We would delay v0.13 for at least a couple weeks to allow for consumers to get deprecation warnings in development)

  1. dispatchMultiple will be true by default internally but no longer can be set as an option. Users that were using it for never ending logic will want to set warnTimeout: 0 instead
  2. throw error in createLogic if process hook definition has dispatch but omits done and the warnTimeout is not set to zero.
    • done must be called when dispatching is finished or never ending logic should set warnTimeout: 0.
  3. throw error in createLogic if dispatchMultiple is set (!production ENV)
    • dispatchMultiple option is no longer needed, set warnTimeout:0 for non-ending logic
  4. retain the warnTimeout w/default of 60s as is (development) to help people from having unintentional non-completing logic.

Error in Component render is reported by createLogicAction$

I don't know if this is a problem with redux-logic, per se. But I do see these kinds of errors so much that I'm curious is there's something that I should be doing better in order to catch them so they createLogicAction$ isn't the end of the line for the error.

2017-05-19_13-09-49

It's important to note that the error here is happening in my VirtualizedGrid's render function, and as best as I can tell, there are no errors within any dispatched actions.

I'd like to be able to to bubble up errors like this to the user through a toast. I have code in place for things like that, but it seems that since the "final stop" for these is in redux-logic, my other code doesn't have a chance to grab it,

Have you or anyone else see issues like this?

call done automatically

Is it possible to call the done callback automatically, if the process function is executed or the returned promise is resolved?
I am revamping my current app to use redux-logic, and this is a very common use case for me.

export const someLogic = createLogic({
  type: SOME_ACTION,
  async process({apiClient}, dispatch, done) {
    const data = await apiClient.getSomeData();
    if (data.something) {
      done();
      return;
    }
    // do more stuff here
    done();
  },
});

It could be converted to this:

export const someLogic = createLogic({
  type: SOME_ACTION,
  autoDone: true,
  async process({apiClient}, dispatch, done) {
    const data = await apiClient.getSomeData();
    if (data.something) {
      return;
    }
    // do more stuff here
  },
});

Such syntax would be much useful for me because there is always a risk to forget to call done().

Idea: allow return of promise or observable to signal completion, enabling free dispatch

One of the potentially confusing parts of the current API is for multi-dispatching.

For the logic to know when it is complete we need to somehow communicate that in our code.

If you dispatch an observable or use processOptions.dispatchReturn (where you can return an object, error, promise, or observable) then we can know from these when the code is done, so that works great for those use cases.

However I don't want to force everyone to use promises or observables so we allow the developer to signal completion by expecting a single call of the dispatch function. Since I think the most common use case is to do a single dispatch, that makes a good fallback plan. However when the user wants to make multiple dispatches then it gets tricky. They need to either dispatch an observable or use the second parameter in the dispatch function dispatch(action, { allowMore: true }) to indicate they aren't done and then the final dispatch without allowMore set to end.

So I think this could be confusing to people once they want to do multiple dispatches and they are not using observables. They would likely not remember they have to do something special to enable that.

After reviewing how redial works, it allows free dispatching but uses a returned promise to indicate completion. Thus it doesn't do anything with the value coming back, but uses that to signal it is done. It is expected that the user will dispatch any values or errors but returning the promise to signal it is done.

This got me thinking that we could do the same thing.

Here's how it could work:

First if processOptions.dispatchReturn is true, then nothing would change, it would still dispatch the object, error, or results from promise/observable returned.

So assuming you need to do multiple dispatches and are not using an observable to do that, then a different process option could enable that.

processOptions: {
  // enables free dispatching if the code returns a promise or observable
  completeReturn: true // complete when the returned promise/observable is done
}

Thus if the code returns a promise or observable that would be used to signal completion. This would enable the dispatch to be free to be used for multi-dispatching. If the user doesn't return a promise or observable, then we'd expect a single dispatch as the way it works today (or they can still override with the allowMore flag).

By allowing free dispatching in the promise code, it makes it easy to dispatch things in different steps of the promise chain as you receive the results.

It is also nice that async functions (async/await) return a promise. So I can imagine many people using that when they need to perform many operations in one set of code. Simply by returning the result of the async function (a promise) they will be able to use free (multiple) dispatching as well.

So I'm currently thinking that this might even be a good default. If they enable dispatchReturn that takes priority and goes the extra step of dispatching for them, but otherwise by default completeReturn could be defaulted to on.

This feature only becomes active if they return a promise or observable, so if they don't then it works as it does today (expecting single dispatch). But once they start returning a promise or observable (or returning from an async function) then we start using that to determine completion.

What do you think? Any thoughts or other ideas?

Add TypeScript definitions

I'm writing an app in TypeScript, and it would be nice to have definitions to aid in syntax completion, etc.

Provided that you're open to this, I'll finish what I've written so far and submit a pull request.

Logic that executes on state change

Hello. Is there a way to configure Logic to fire when some deep state changes?

It would be a different approach where instead of running business code on FETCH_POLLS there would be only dispatched action like FETCH_POLLS_START which would set state polls.fetching to true. A Logic would run when it would detect transition of polls.fetching from false to true. This would bring interception based not on action type but on state change, which is even more general.

There are projects like redux-watch which solves only this part. Could it be conbined with redux-logic?

What do you think?

Duplicate success process options calls due to options request?

I've noticed a weird bug in an application using redux-logic. I'm using axios to post to a third party API. Using developer tools, the request shows up twice under the network tab. Options and the final post.

I suspect the success process options is triggering twice as a result of the options request.

I'm having a difficult time replicating the issue using the reqres.in service thou.

Is this a possibility or could it be related to another bug deeper in my code base.

Many thanks.

Dispatch is undefined in logic

createLogic({type: actions.UPLOAD, latest:true, process({action}, dispatch, done){ console.log("dispatch: " +JSON.stringify(dispatch)) });

returns undefined for dispatch.
Please help.
Thanks

warnTimeout: 0 and store.whenComplete()

I am using redux-logic-test for testing.
I have a logic that initializes the socket and dispatches actions.
warnTimeout is set to 0.

In my unit tests, I call await store.whenComplete(), but it causes timeouts because the logic never calls the done() callback.

If I call done(), all actions are ignored when I call dispatch({...}).
Is there any workaround for this?

Unhandled Exception in createLogicAction$.js

Hi there, I am seeing the attached error occurring in my React-Native app:

img_0033

The error occurs in the following sequence

  1. Log in the user which gets the data from firebase - with allowMore: true to get continuous updates from firebase using the logic shown below.
  2. Logout the user
  3. Log in again, which re-executes the same logic - this triggers the error

The referenced logic is

export const fetchMarkersLogic = createLogic({
  type: MARKERS_FETCH_REQUEST,
  latest: false, // take latest only

  process({ firebase, action }, dispatch) {
    const { currentUser } = firebase.auth();
    firebase.database().ref(`/markers/${currentUser.uid}`)
      .on('value', snapshot => {
        const markers = _toArray(snapshot.val());
        dispatch(
          { type: MARKERS_FETCH_SUCCESS, payload: markers },
          { allowMore: true } // Allow multiple dispatch
        );
      })
    ;
  }
});

When logging out the user, I've tried executing dispatch() and a dispatch with allowMore: false, but the error still occurs.

Any ideas?

Request: Allow funcs as dependencies that take store as a parameter

Firstly, just wanted to say that I'm loving the library.

Being able to pass a dep into createLogicMiddleware is really useful , especially for testing scenarios, however there are scenarios where the dependency needs access to the store.

For instance we have a api dependency, however the access token it needs is contained in the store. Right now, within the logic we have to set the access token on api by using getState, it would be nice if we could register api in deps as a function that takes state and returns the api.

Unit testing, how can we perform process testing using async and await keywords.

Following #21,

The example you provided works for the promise chaining processing style.

This is not the writing you recommanded to me, I have this two process that require unit test:

Global logic of logout, will be present on every pages:

export const getLogoutLogic = createLogic({
  type: SUBMIT_LOGOUT_REQUEST, // trigger on this action
  latest: true, // use response for the latest request when multiple
  async process({ pages, authService, forwardTo }, dispatch, done) {
    try {
      const success = await authService.logout();
      dispatch(sendingLogoutRequest(false));
      forwardTo(pages.pageLogin.path);
      dispatch(onSuccessLogoutRequest(success));
    } catch (err) {
      dispatch(sendingLogoutRequest(false));
      forwardTo(pages.pageLogin.path);
      dispatch(onErrorLoginRequest(err));
    }
    done();
  },
});

Login logic, will be present on LoginPage:

export const getAuthorizeLogic = createLogic({
  type: SUBMIT_LOGIN_REQUEST, // trigger on this action
  cancelType: LOCATION_CHANGE, // cancel if route changes
  latest: true, // use response for the latest request when multiple
  async process({ authService, forwardTo, pages, action }, dispatch, done) {
    const { username, password } = action.data;
    try {
      const jwt = await performLogin(authService, username, password);
      dispatch(onSuccessLoginRequest());
      dispatch(jwtLoaded(jwt));
      forwardTo(pages.pageDashboard.path); // Go to dashboard page
    } catch (err) {
      dispatch(onErrorLoginRequest(err));
      forwardTo(pages.pageLogin.path); // Go to dashboard page
    }
    done();
  },
});

async function performLogin(authService, username, password) {
  await authService.preLogin();
  await authService.login(username, password);
  const codeRes = await authService.code(oauthClient.clientId, oauthClient.redirectUri);
  const code = getParameter('code', codeRes.url);
  const jwt = await authService.token(oauthClient.clientId, oauthClient.clientSecret, code, 
  return jwt;
}

In your example:

const resultPromise = getAuthorizeLogic.process({ 
    pages, 
    authService, 
    action, 
    forwardTo, 
    getState, 
    requestUtil 
}, dispatch, done);

Even with a mock for my test, this doesn't return a promise, but instead undefined because I don't do any return in my process.

I am also surprise there is no otherway to test the code "line by line", for example, for await return value checking.

I also need a way to test when errors happen, so my app doesn't get unsynced with it's store.

Thanks in advance.

Feature Request: catchMiddleware request

I need to dispatch a logout event when I receive a err.statusCode = 401.

So far, I have two solutions available:

  1. write this on each logic: inconvenient
  2. Pass the dispatch to my fetch wrapper function, then dispatch the action from: not the place to do it and also inconveniant

You recommended us in the documentation to use Observable to cancel an ajax call.
Since fetch doesn't have a way to cancel an ajax request, and also can't be configured globally, I think it will be a nice feature if you add the possibility to catch globally the logic errors.

Process function fails to execute when using the validate method

Hi guys, I have been looking at this issue for an hour now and I can't seem to find the issue here.

So have piece of logic that first validates the payload before executing the process function. But when the process method executes it fails to dispatch another action. When I comment out the the validation method the process function seems to be running fine. I have tested if the process does get called when the validation method runs and it seems it does it executes the post method and resolves the promise but it fails to dispatch.

Here is the logic that I wrote

  type:${NAME}/${REGISTER},
  latest: true,
  validate({getState, action}, allow, reject){
     let {email, password, confirmPassword} = action.payload
     let {isEmpty, isEmail} = validator
     let state  = getState()
      if(isEmpty(email)){
state[NAME] = {
            ...state[NAME],
            forms:{
              ...state[NAME].forms,
              isLoading : false,
              isSuccess: false,
              isError: true,
              responseMessage: 'Email is required'
            }
         }
         reject(action)
       }
       else if( !isEmpty(email) && !isEmail(email))
       {
           state[NAME] = {
                 ...state[NAME],
                 forms:{
                   ...state[NAME].forms,
                   isLoading : false,
                   isSuccess: false,
                   isError: true,
                   responseMessage: email + ' is not a valid email!'
                 }
            }
   reject(action)
       }
      else if( isEmpty(password))
        {
        state[NAME] = {
          ...state[NAME],
          forms:{
            ...state[NAME].forms,
            isLoading: false,
            isError : true,
            isSuccess: false,
            responseMessage: 'Password is required'
          }
        }
        reject(action)
      }
      else if( !isEmpty(password) && isEmpty(confirmPassword)){
           state[NAME] ={
             ...state[NAME],
             forms:{
               ...state[NAME].forms,
               isLoading:false,
               isError: true,
               isSuccess: false,
               responseMessage:'Please confirm your password'
             }
           }
 reject(action)
      }
      else if( !isEmpty(password) && !isEmpty(confirmPassword))
      {    if(!(password === confirmPassword))
            {
              state[NAME] ={
                ...state[NAME],
                forms:{
                  ...state[NAME].forms,
                  isLoading:false,
                  isError: true,
                  isSuccess: false,
                  responseMessage:'Password Confirmation does not match'
                }
              }
            }
            reject(action)
          }
    allow(action)
  },
processOptions:{
    dispatchReturn: true,
    successType: ${NAME}/${REGISTER_SUCCESS},
    failType: ${NAME}/${REGISTER_FAILURE}
  },
process({getState, action,http}, dispatch){
 const apiURL = ctx.flags.__DEV__
   ? 'http://${ctx.options.app.domain}:9002'
   : ''
 let dataObj  = JSON.stringify({
     email: action.payload.email,
     password: action.payload.password
   })
  return http.post(apiURL+ '/users', dataObj, {
    headers: {
      "Content-Type":"application/json"
    }
  })
  .then(res=>{
     return res
  })
  .catch(
    err =>{
       console.log('PROMISE REJECTED')
      return err
    })
  },
})```

Question: Why not make dispatch available in transform?

Hi @jeffbski,

I have a use case where given an action I might modify it, reject it, or do nothing to it. However, when I reject it I want to dispatch a new action in order to have some information persisted to the state.

I notice that transform only has the next/reject methods available to it, is there a technical reason why dispatch is not there?

Thanks for your time,
Ivo

use logic to update component state

It's probably anti design pattern, but because of the all the utilities in it, I found it pretty convenient to store my async logic using redux-logic.

I have a case where I need to display an html table with async data.

The example case is the HAL table that needs to fetch it's data to populate it's content.

https://github.com/spring-guides/tut-react-and-spring-data-rest/blob/master/security/src/main/js/app.js#L29

At the end the component is updated with :

		this.setState({
			page: this.page,
			employees: employees,
			attributes: Object.keys(this.schema.properties),
			pageSize: pageSize,
			links: this.links
		});

I wonder if I can move this in a logic so I can use async, await and all my utilities.

Can I use redux-logic for updating the component state ?

wait for dispatch

I am using redux-connect to load initial data.
When using thunks, I can simply do:

const resolve = [{
  promise: ({params, store}) => store.dispatch(init(params.layout))
}];

store.dispatch returns a promise.

What's the proper way to implement it in redux-logic?
Below is my solution, but I think there should be more elegant way to do it.

const resolve = [{
  promise: ({params, store}) => {
    const defer = {};
    defer.promise = new Promise((res, rej) => {
      defer.resolve = res;
      defer.reject = rej;
    });
    store.dispatch(init(params.layout, defer));
    return defer.promise;
  },
}];

The logic handler calls defer.resolve() before calling done().
init() is a simple action creator.

Webpack cant resolve module 'babel-runtime'

Hi there, I've just started using this library and its going well except for an issue that I haven't been able to resolve. I'm not sure what's causing it, but it started when I installed this library and the required RXJs libraray, and updated WebPack (1.13.2) and Babel (core 6.14) to handle the ES2016 features.

The babel-runtime is in dependencies, babel-plugin-transform-runtime is in devDependencies, I've tried clearing the NPM cache, removing and reinstalling node_modules, changing parameters on the webpack CommonsChunkPlugin, .bablerc, etc for 2 days and the exception is still thrown in the browser. Even odder, the application seems to be working, but I cant have this browser exception.

Here is the output from webpack --display-error-details

> webpack --display-error-details

clean-webpack-plugin: /Applications/MAMP/htdocs/tonight/build has been removed.
Hash: 5d841416e86a960afb3d
Version: webpack 1.13.2
Time: 58269ms
                           Asset       Size  Chunks             Chunk Names
                      robots.txt   26 bytes          [emitted]
  vendor.b47001ac2219ce9e5fe0.js    3.09 MB    0, 2  [emitted]  vendor
manifest.7f098e6c96f193b21664.js  763 bytes       2  [emitted]  manifest
    app.edc2af931ca5a3a1776b.css    16.2 kB    1, 2  [emitted]  app
                     favicon.ico    1.15 kB          [emitted]
                      index.html    2.04 kB          [emitted]
     app.edc2af931ca5a3a1776b.js    2.39 MB    1, 2  [emitted]  app
           styles/icon-style.css    1.14 kB          [emitted]
               fonts/icomoon.svg    8.34 kB          [emitted]
               fonts/icomoon.eot    3.26 kB          [emitted]
              fonts/icomoon.woff    3.18 kB          [emitted]
               fonts/icomoon.ttf     3.1 kB          [emitted]
   [0] multi vendor 496 bytes {0} [built] [1 error]
    + 2483 hidden modules

ERROR in multi vendor
Module not found: Error: Cannot resolve module 'babel-runtime' in /Applications/MAMP/htdocs/tonight
resolve module babel-runtime in /Applications/MAMP/htdocs/tonight
  looking for modules in /Applications/MAMP/htdocs/tonight
    /Applications/MAMP/htdocs/tonight/babel-runtime doesn't exist (module as directory)
    resolve 'file' babel-runtime in /Applications/MAMP/htdocs/tonight
      resolve file
        /Applications/MAMP/htdocs/tonight/babel-runtime doesn't exist
        /Applications/MAMP/htdocs/tonight/babel-runtime.js doesn't exist
        /Applications/MAMP/htdocs/tonight/babel-runtime.jsx doesn't exist
  looking for modules in /Applications/MAMP/htdocs/tonight/node_modules
    resolve 'file' babel-runtime in /Applications/MAMP/htdocs/tonight/node_modules
      resolve file
        /Applications/MAMP/htdocs/tonight/node_modules/babel-runtime is not a file
        /Applications/MAMP/htdocs/tonight/node_modules/babel-runtime.js doesn't exist
        /Applications/MAMP/htdocs/tonight/node_modules/babel-runtime.jsx doesn't exist
    resolve 'file' or 'directory' /Applications/MAMP/htdocs/tonight/node_modules/babel-runtime
      resolve file
        /Applications/MAMP/htdocs/tonight/node_modules/babel-runtime is not a file
        /Applications/MAMP/htdocs/tonight/node_modules/babel-runtime.js doesn't exist
        /Applications/MAMP/htdocs/tonight/node_modules/babel-runtime.jsx doesn't exist
      resolve directory
        directory default file index
          resolve file index in /Applications/MAMP/htdocs/tonight/node_modules/babel-runtime
            /Applications/MAMP/htdocs/tonight/node_modules/babel-runtime/index doesn't exist
            /Applications/MAMP/htdocs/tonight/node_modules/babel-runtime/index.js doesn't exist
            /Applications/MAMP/htdocs/tonight/node_modules/babel-runtime/index.jsx doesn't exist
[/Applications/MAMP/htdocs/tonight/babel-runtime]
[/Applications/MAMP/htdocs/tonight/babel-runtime]
[/Applications/MAMP/htdocs/tonight/babel-runtime.js]
[/Applications/MAMP/htdocs/tonight/babel-runtime.jsx]
[/Applications/MAMP/htdocs/tonight/node_modules/babel-runtime.js]
[/Applications/MAMP/htdocs/tonight/node_modules/babel-runtime.js]
[/Applications/MAMP/htdocs/tonight/node_modules/babel-runtime.jsx]
[/Applications/MAMP/htdocs/tonight/node_modules/babel-runtime.jsx]
[/Applications/MAMP/htdocs/tonight/node_modules/babel-runtime/index]
[/Applications/MAMP/htdocs/tonight/node_modules/babel-runtime/index.js]
[/Applications/MAMP/htdocs/tonight/node_modules/babel-runtime/index.jsx]

Here is .babelrc

{
  "presets": [
    "es2015",
    "es2016",
    "react"
  ],
  "plugins": [
    ["transform-runtime", {
      "polyfill": false,
      "regenerator": true
    }],
    "syntax-async-functions",
    "transform-class-properties",
    "transform-regenerator",
    "transform-object-rest-spread"
  ]
}

Any help is appreciated

cancelling by id

Our usecase is that we add toasts with payload { "type": "TOASTS_ADD", "payload" { "id": <toast_unique_id>, "timeout": <optional_timeout>, "message": <message> } } and remove them with payload { "type": "TOASTS_REMOVE", "payload": <toast_unique_id>}. Removal is done manually or on timeout.

If it is done manually then we would like to cancel logic (waiting on toast timeout) by <toast_unique_id>. From API it seems that only type can be used for cancellation, without any payload support.

Therefore we've created "workaround":

const clearTimeoutMap = new Map();

const logic1 = createLogic({
  type: 'TOASTS_ADD',
  process({ action: { payload: { timeout, id } } }, dispatch, done) {
    if (typeof timeout === 'number') {
      const to = setTimeout(() => {
        clearTimeoutMap.delete(id);
        dispatch(toastsRemove(id));
        done();
      }, timeout);

      clearTimeoutMap.set(id, () => {
        clearTimeoutMap.delete(id);
        clearTimeout(to);
        done();
      });
    } else {
      done();
    }
  },
});

const logic2 = createLogic({
  type: 'TOASTS_REMOVE',
  process({ action: { payload: id } }) {
    const ct = clearTimeoutMap.get(id);
    if (ct) {
      ct();
    }
  },
});

Is this approach OK?

Question: Process similar events in order

I have a use case where actions are to be processed in order in the following way:

Event order:
---a1---a2---a3--b1---b2--b3--a4---b4

Processing Order
---a1---a2---a3----------------a4------
-----------------b1---b2--b3---------b4

All actions need to be processed in order and a2 should be dispatched to Redux logic only after a1 has been processed by reducer and the state of 'a' is updated in store. a1 can also have some side effects that could in turn generate a11, a12 actions. which again have to be processed in order. After all the events with respect to a1 have been processed by reducers and state updated in store, I want a2 to be dispatched to redux-logic. Similarly I want to process 'b' events without blocking for a.

Is the above possible?

Firebase database automatic refresh dispatch not working on React Native

Hi there,
I'm building a test app with RN that uses the redux-logic library. I have several pieces working, but I recently ran into an issue that I can't resolve.

I'm using the firebase database 'on' event, that will automatically trigger whenever the backend database is changed. For example, I have a db of users, and if I add a new user the event is triggered, which does a dispatch to update my redux store.

Here is the docs on the FB call:

https://firebase.google.com/docs/database/web/read-and-write

Here is the code snippet for my logic:

export const employeesFetchLogic = createLogic({
  type: EMPLOYEES_FETCH_REQUEST,
  latest: false, // take latest only

  // firebase injected
  // from configureStore logic deps
  process({ firebase }, dispatch) {
    const { currentUser } = firebase.auth();

    //  Get all the employees from firebase
    firebase.database().ref(`/users/${currentUser.uid}/employees`)
      .on('value', snapshot => {
        // THIS DISPATCH ONLY WORKS THE 1ST TIME!
        dispatch({ type: EMPLOYEES_FETCH_SUCCESS, payload: snapshot.val() });
      })
    ;
  }
});

It works fine the 1st time its called after I add a new record when the app 1st loads, but the dispatch is not passed through to my reducer the second or subsequent times. I stepped through the debugger and can see the event being triggered every time and the reducer being called the 1st time, but the dispatch is not passed through to the reducer after the 1st time.

Any ideas why this could be?

regards,
Royce

Feature Request: waitFor

Enhance the validate block with a waitFor option.

An example:

During init, an application validates a user's session and populates auth state. Should a redux-logic function depend on auth state but execute prior to auth processes being processed, the validate block can delay execution till a known action is received.

redux-logic: with no RxJs

I love redux logic. Dude, this is great. I have a problem though... I'm addicted to making things very tiny. I know. It doesn't matter. I know size in the JS world is not really a problem. Most apps are just images anyway. But still. Before I go and do something stupid:

(1) if I built this LIB without RxJs/Observables what problems would I run into
(2) could I build a tiny faximily library and still handle things like (debouncing and throttling) without RxJS
(3) great work, I love this library.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.