Giter Club home page Giter Club logo

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

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?

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..

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

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 ?

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!

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?

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;
})

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?

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

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.

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?

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
    })
  },
})```

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?

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

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 ?

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 ?

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.

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

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.

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.

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.

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 ?

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.

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()

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!

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?

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

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.

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.

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?

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?

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.

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?

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.

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.

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().

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.

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?

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.

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

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
}

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?

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

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?

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.