Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

machines: follow some react/redux best practices #9461

Closed
wants to merge 2 commits into from

Conversation

KKoukiou
Copy link
Contributor

@KKoukiou KKoukiou commented Jun 21, 2018

Note: The two first patches of the series are not relevant and PR will be rebased on top of https://github.com//pull/9166 once it will be merged.

Since more contributors are/will be coming to cockpit machines, let's make the package a bit more friendly for new comers by following some of the best practices mentioned in he official docs. (See https://redux.js.org). This will involve unfortunately some code move but IMO it's better to do it now, than wait for more features to be introduced and the project to get bigger.

As a start I posted two patches:

  • First creating constant for actions types, see why here https://redux.js.org/recipes/reducing-boilerplate#actions
  • Second splitting the actions.es6 file and sorting the functions in alphabetical order because it was getting really big, and there it was not clear if there was some pattern on where new actions creators should be placed inside the file.

I would like to see some organization in the components directory as well, to make it clear which are Presentational and which Container Components but let's keep it for another PR.

For new contributors it will be helpful as well, if we have clear naming conventions for actions, actions types and selectors.
I appreciate this one:

action name: <NOUN>_<VERB>
action creator name: <verb><Noun>
selector name: get<Noun>

See reasoning here: https://medium.com/@kylpo/redux-best-practices-eef55a20cc72
But I would be ok to keep the current (followed in most cases) which is:

action name: <VERB>_<NOUN>

In any case let's keep it documented.

@mareklibra
Copy link
Contributor

Thank you for cleaning this up.
Scope and reasoning seems to be clear, I have no review comments so far.

@KKoukiou
Copy link
Contributor Author

KKoukiou commented Jun 22, 2018

@mareklibra I actually have one question.

So, the actions and action creators are logically divided into two categories,

  • the store ones ( which are actually going to be handled by reducers to make changes to the store) - - - the provider ones

The provider action creators are actually always resulting in dispatching actions from the store-actions-types, since the store action types are the only ones that have reducers. In addition, all the provider-actions-types are not actually actions since they never appear in any action object.
From the docs:
Actions are plain JavaScript objects. Actions must have a type property that indicates the type of action being performed.
aka look like this:

{
  type: MY_ACTION,
  ...
}

I understand that you wanted somehow to encapsulate all these async operations that these functions are performing, thus you decided to create these fake action-creators for them. But for a new comer to the project, who is not familiar with redux this is really confusing to have in the action-types something that is never used as a function.
So, unless I am getting something wrong 🤔 , maybe

  • we should move all the provider-actions to other files, to stop suggesting that they are actions.
  • else we can actually use come middleware like promiseMiddleware, and actually create actions from these functions. Then we can have more fine grained actions, specific to each operation.

WDYT?

@mareklibra
Copy link
Contributor

@KKoukiou , I see same differentiation of the actions as you mentioned, but in my understanding the separation is more about whether an action has external effect or just manipulates local data. This results in the same split as described above, but the way of thinking is slightly different.

So I'm fine with splitting these actions to multiple files.

Important point for me is the way how an action is emitted. The consuming code (means React) should "just dispatch an action" if whatever has changed or needs to be done. This is enabled by the thunk middleware which logic/implementation seems super-simple to me.

Right, we can wrap the actions to plain JS data objects as we do for the second type of actions, but I don't think this will bring any benefit for us.
Simple calls are handled via single (and already simple) virt() method. Further, the provider code can just return functions and they are handled same way.

@KKoukiou
Copy link
Contributor Author

@mareklibra thanks for reply. I also added one more commit, which defines helper functions for the action creators. I think it's a good idea in order to force the FSA standard since now half actions are following FSA and half are not. WDYT?

*/

function makeActionCreator(type, ...argNames) {
return function (...args) {
const action = { 'type': type }
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: const action = { type }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor

@mareklibra mareklibra left a comment

Choose a reason for hiding this comment

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

I'm fine with action creator helper functions. No matter I do not see writing of action creator methods as a big deal, since they are quite straightforward to write even without any helper function.
I don't think there's so much boilerplate in writing them "from scratch", the complexity is somewhere else, imo.
And considering KISS, their content is clear at the first glance.

I must admit, that I leverage recent ("from scratch") form while debuging - I can easily add check or log there. This will be more complicated when wrapped. But please note, this is not a blocker, just a note regarding workflow.

In general, the use of non-FSA actions is just a historic left-over in the Cockpit codebase.
IMO, we can start with refactoring of all actions to FSA and not differentiate them.

There should not be much logic within action creators. But I found quite useful to implement some "translation or unification" logic within them occasionaly. Nothing complicated but just making the calls simpler.

Regarding recent implementation:
The contract for FSA and non-FSA is different - the FSA action creator will consume a single object, while non-FSA list of args.
If improvement is needed, I would start with unification of this.

function makeActionCreatorFSA(type, ...argNames) {
return function (...args) {
const action = { 'type': type, 'payload': args[0] }
return action
Copy link
Contributor

Choose a reason for hiding this comment

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

Can be one-line statement

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


function makeActionCreatorFSA(type, ...argNames) {
return function (...args) {
const action = { 'type': type, 'payload': args[0] }
Copy link
Contributor

Choose a reason for hiding this comment

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

similar

Copy link
Contributor

Choose a reason for hiding this comment

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

The returned function expects just a single argument. If this would be the way to go, let's make it clear at the first glance.

@KKoukiou
Copy link
Contributor Author

@martinpitt I rebased, please remove needs-rebase label.

*/

// --- Store actions --------------------------------------------
export const ADD_NOTIFICATION = "ADD_NOTIFICATION";
Copy link
Member

Choose a reason for hiding this comment

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

I can't say I like this very much.. it is yet another place to change when one adds new functionality (but then again with redux this is already ridiculously heavily split), and it makes things harder to debug (as you see numeric values).

But I don't veto this if @mareklibra agrees - at this point the "machines" code is maintained by your team mostly anyway, so you can pick the code style.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@martinpitt I don't get your point by "and it makes things harder to debug (as you see numeric values)." Where you 'll see the numeric values?
I don't really see anything against this split. Benefits can be seen here. https://redux.js.org/recipes/reducing-boilerplate#actions
In general I would like to keep somehow naming consistent, and having all action type in one file makes it easier to review.
But I will not insist.

Copy link
Member

Choose a reason for hiding this comment

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

Where you 'll see the numeric values?

I didn't actually see them, but won't they appear in stack traces and similar views, instead of the string constants?

But as I said, I don't veto this for the machines page, it was just a comment.

@KKoukiou
Copy link
Contributor Author

Getting rid of of last commit for action creator helper functions, reconsidered its usefulness.

KKoukiou added 2 commits July 25, 2018 17:12
Since strings are prone to typos and duplicates this approach
helps avoiding errors that will be difficult to debug.

Read more about benefits of this approach here:
https://redux.js.org/recipes/reducing-boilerplate#actions

Signed-off-by: Katerina Koukiou <[email protected]>
actions.es6 file contains both provider and store actions.
Let's keep them in seperate files to increase readability
and make it easier for new maintainers to navigate through
the package.

Together with the move the functions were also sorted in
aphabetical order.

Signed-off-by: Katerina Koukiou <[email protected]>
@martinpitt
Copy link
Member

I merged this with fixing two missing semicolons (fatal now after #9736), and dropping the Signed-Off-By. Thanks!

@KKoukiou KKoukiou deleted the cleanup-machines branch November 29, 2018 11:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants