Skip to content

Make combineActions works with nested handleActions #294

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 2 commits into from
May 15, 2018

Conversation

Kerumen
Copy link
Contributor

@Kerumen Kerumen commented May 3, 2018

Fixes #241 (I used the code @Parakleta suggested)

It allows nesting of actions in handleActions after a combineActions (very useful for loadings):

const reducer = handleActions(
  {
    [combineActions(apiCall1, apiCall2)]: {
      LOADING: () => ({ loading: true }),
    }
  },
  { loading: false }
);

I had some troubles to write a clean test, let me know if you want to improve it.

@codecov
Copy link

codecov bot commented May 3, 2018

Codecov Report

Merging #294 into master will increase coverage by 0.15%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #294      +/-   ##
==========================================
+ Coverage   95.45%   95.61%   +0.15%     
==========================================
  Files          14       14              
  Lines         110      114       +4     
  Branches       36       36              
==========================================
+ Hits          105      109       +4     
  Misses          4        4              
  Partials        1        1
Impacted Files Coverage Δ
src/utils/flattenWhenNode.js 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 652668c...9859b25. Read the comment docs.

@timche
Copy link
Member

timche commented May 3, 2018

Thanks! I'll look into this asap.

Copy link
Member

@timche timche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain me from an API perspective what exactly is being added here and why that isn't possible now? I was out of this library for longer time and many things have been added since then. After reading #241 and your example I'm not 100% sure and also using the term "nested" in combination with handleActions and combineActions confuses me a little bit. Thanks.

I think it's best if you could update the documentation accordingly.

}
});

// NOTE: We should be using combineReducers in production, but this is just a test.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this comment mean, because

  1. combineReducers doesn't exist
  2. if combineActions is meant, it is being used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just copy-pasted the previous test 🙃

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we can remove it as I don't see that it makes any sense.

@Kerumen
Copy link
Contributor Author

Kerumen commented May 4, 2018

@timche To be honest, I wanted to use a "nested" handleActions with combineActions. I found out it wasn't working (only the last action was handled) so I search through issues and found the #241. Someone provided a code solution and after some tests in my local project, that was perfectly working. I filled this PR.

Currently, if you write your handleActions like this:

handleActions({
  NOT_NESTED: function,
  [combineActions(action1, action2)]: {
    NESTED: function,
  }
})

Only action2/NESTED will be handled. Not action1/NESTED.

@timche
Copy link
Member

timche commented May 4, 2018

Thanks @Kerumen for explaining. I'm not questioning your pull request, it's totally fine and appreciate it. Please don't get me wrong, I'm just trying to understand and to make everyone clear what we are trying to achieve as this must also be documented in the documentation and change log. Also regarding the left-over comment in the test, it doesn't mean we can't do better and fix it, right?

Do I understand correctly that combineActions is not working as expected and should be considered as a bug or is this just adding a new feature where we can additionally pass an object beside a function and it was just working with the last action accidentally?

@Kerumen
Copy link
Contributor Author

Kerumen commented May 4, 2018

I don't know how the library was designed. I'm sure that this is not documented for combineActions. But nested handleActions works and is documented:

If reducerMap has a recursive structure, its leaves are used as reducers, and the action type for each leaf is the path to that leaf.

So a combination of both should work?

@timche
Copy link
Member

timche commented May 4, 2018

Alright, so it really sounds more like a bug to me. So we are basically fixing combineActions to work with handleActions as advertised.

DECREMENT: amount => ({ amount: -amount })
}
}
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check the double usage of createAction in L536 and L542. Shouldn't it be only at L542? counter and sameCounter can be destructured there instead too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes maybe I should rename the actions of L536. In my app I have an API call (which is an action) and I handle SUCCESS, LOADING.. with combineActions.
So I dispatch API_CALL and handle API_CALL_SUCCESS. This is why I have 2 createActions. I tried to reproduce this here but maybe it's confusing. 😐

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, but still couldn't you just remove L536 and do this instead?

const { app } = createActions({
  APP: {
    COUNTER: {
      INCREMENT: amount => ({ amount }),
      DECREMENT: amount => ({ amount: -amount })
    },
    SAME_COUNTER: {
      INCREMENT: amount => ({ amount }),
      DECREMENT: amount => ({ amount: -amount })
    }
  }
});

const { counter, sameCounter } = app

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@timche This doesn't work because of line L558: combineActions(counter, sameCounter). combineActions expect actions, not keys. I can rewrite L536 with:

const app: { counter, sameCounter } = createActions('APP/COUNTER', 'APP/SAME_COUNTER')

..if you prefer.
Or totally change action names and do something like API_CALL and API_CALL_SUCCESS?

I know this is not very clear but the initials dispatched (and combined) actions have to be different than the one nested in the combine.

Copy link
Member

@timche timche May 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, didn't have much time to look into that and try it out myself. I just want it to be changed so the code makes sense. Doesn't matter how and to what, it just must be readable and clear.

Copy link
Member

@timche timche May 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still not very clear from my perspective because of using undefined in API_CALL_1: { LOADING: undefined }. Why is it defined as undefined? It just doesn't look right and is also not documented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's totally documented: https://redux-actions.js.org/api-reference/createaction-s#createaction-type-payloadcreator and also used in an example below

SET: undefined // given undefined, the identity function will be used

But if you prefer I can write createAction('API_CALL_1/LOADING'). This way, I use explicitly the / separator which is implicit in the object form.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that undefined will use the identity function instead, but I'm more concerned about using createActions twice and the first one is just being used for creating action types and the second to create action creators which is overall looks quite confusing imo.

Copy link
Contributor Author

@Kerumen Kerumen May 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know.. As I said, in a real app, the LOADING action would have been created else where (in a middleware for example) so it won't be confusing.

However, this is a simple test ensuring the fix works, maybe we can pass through and improve it later?

I've changed it a few times and I don't have many options left. If you propose me something, I would be happy to include it, I'm currently out of ideas.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think this is too much nitpicking from my side, sorry. After all, this issue is fixed. Thanks!

Copy link
Member

@timche timche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to merge it asap when finished discussing about the test.

@Kerumen
Copy link
Contributor Author

Kerumen commented May 13, 2018

@timche Friendly ping, I made a comment in the diff :)

@Kerumen
Copy link
Contributor Author

Kerumen commented May 14, 2018

@timche I've renamed the variables to better match my app and my intention.
Here, it's weird because the LOADING action is created next to the initial action. In a real app, the LOADING would have been created else where (in a middleware for example).

Is that clearer? Sorry for that, I can't find a proper way to write this test..

@timche timche merged commit 0d1b1f7 into redux-utilities:master May 15, 2018
@Kerumen
Copy link
Contributor Author

Kerumen commented May 24, 2018

@timche Can you publish a new version with this change on npm please? Thanks!

@timche
Copy link
Member

timche commented May 27, 2018

Sorry for the delay. Published.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants