-
-
Notifications
You must be signed in to change notification settings - Fork 15.3k
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
examples - how to implement action type namespacing ? #786
Comments
Put accountType into actions? See also Similarly don't forget action creators are just functions, and you can create functions that return a bunch of other functions. |
I think it would really help if you provided a specific small example of the problem you say is ignored in existing examples. We can then help you find ways to simplify that specific example. It's very hard to suggest something specific in response to a question without code. |
@gaearon I know where you're heading with this, but I am not trying to create reusable reducers, action creators or DRY things up, quite the opposite. I am trying to isolate groups of actions from each other so I can have 2 people working on two different sections of the system and be sure that they won't start triggering each other's reducers accidentally due to lack of namespacing in the action type names. Hope this makes it more clear. |
I've solved this issue in my project(s) by using https://www.npmjs.com/package/flux-constant in combination with a super simple helper function called Essentially, my code ends up being: // create-action-types.js
var fluxConstant = require('flux-constant');
module.exports = function (types) {
return fluxConstant.set(types);
};
// in foo-action-types.js
module.exports = createAtionTypes([
'ADD_FOO',
'REMOVE_FOO'
]);
// in some-store.js
var fooActionTypes = require('foo-action-types');
function (state, action) {
switch(action.type) {
case fooActionTypes.ADD_FOO:
case fooActionTypes.REMOVE_FOO:
}
} |
Why not keep all action types as constants in one place? |
To better visualise this, let's say this is the initial state of the store: let initialState = {
"testerAccount": {
"videoUploads": [],
"messages": []
},
"customerAccount": {
"videoUploads": [],
"messages": []
},
"systemUserAccount": {
"videoUploads": [],
"messages": []
}
}; How will you avoid action type name collisions when implementing separate reducers for adding videos or messages for each of these sections ? |
@gaearon what you're suggesting doesn't solve the core of the problem, it's more of duct tape approach because over time you will end up with a huge type file which will cause a lot of problems . It will straight away cause problems during code merges which later on will need to be solved with naming hacks, for e.g.: ADD_VIDEO_UPLOAD, ADD_TESTER_VIDEO_UPLOAD, ADD_VIDEO_UPLOAD_IN_SOME_SECTION, etc. which is a one big headache again. @koulmomo This is exactly what I was looking for, thank you :) 👍 Simple and powerful. @gaearon I think we should have at least one example which uses either this package or a new package which could be named redux-constant ? In my opinion promoting approach in the docs / examples, which solves the problem of namespacing should be the new default. Thank you for your help with this :) |
In your example, I don't really get why not have one set of types, one reducer generator and one set of action creators. The actions would contain |
I don't think People often try to make Flux “simpler” without realizing they are breaking its essential features. |
I was mislead by the apparent symmetry in your example. I understand now that you meant there is no DRY here, and the symmetry in features is only apparent as you said in #786 (comment). |
I don't think there's a need for a library here. Establish a convention! For example, if your subprojects or features are so separate, make it a rule to call action types Violations will be easy to catch in code reviews. If you really want to, you can automate this but I can't see what it brings over manual namespacing. Inside each module, you'd still want to declare all constants in one file for easier control over features, preventing accidental duplication of effort, and as a documentation. I'm all for adding a |
Strings aren't inherently bad for namespacing. URLs are strings, and they seem to work fine. |
@gaearon You are right , I forgot about the problem of serialization with the solution based on flux-constant. I don't have a problem with string based namespacing as long as we can easily distinguish the modules / namespaces in the name it self. The naming convention based solution is something I have seen before: https://github.com/erikras/ducks-modular-redux but I hoped that there might be a better or a "correct" way of doing this which would be based on your experience. I think I will try to convert the flux-constant approach into something that you could actually serialize in some way. Please add a "huge-apps" example if you find time for this because this will save other people a lot of time and maybe it will lead to establishing some sort of convention which we could easily follow later on. Thank you again :) |
Yeah, sorry, unfortunately I haven't been working on a big app for some time, and even when I did, I actually liked that we have a single giant file with constants grouped into sections because it provides a good overview of what may happen in the app. |
I'd look to see this "problem" explained in more detail in the docs. I have/had the same questions like @pbc. I don't think "Why not keep all action types as constants in one place?" works if you re-use multiple modules across projects and the recommendation "Establish a convention!" sounds very much like something like BEM in CSS land. However we move away from BEM to CSS modules and I guess something similar to CSS modules which hash class names is needed for action types in big projects, too. |
As far as I can tell, there is no way to achieve both serialization and absence of conflicts. A good convention for reusable modules might use already existing "uniqueness providers", like reverse domains names, or username/repo on github, or registered module name on npm. EDIT: CSS modules does this by defining a custom language on top of CSS and prefixing the classnames during preprocessing (boils down to convention anyway, but a generated one). |
+1 for the question and the discussion. I also struggled a lot with this exact problem as @pbc . Confusion for me is that we have combineReducers for reducers, which makes a nice state tree, but actions on the other hand feel more or less like a global. By the looks of it, any type of namespacing has to be done manually. |
I think I've found a solution for serializable string action types. I think it's one of those situation where our minds impose artificial limitations on something. The basic idea is that the type constants don't need to be related in any way to the string value. So you can use randomly generated values, hashed file paths, or anything else unique for the type constant string value. In your reducer, you import the type constant by name, and compare it by name. That name might be used by other actions and in other reducers, but it doesn't matter, because the value won't be the same. Example here: https://gist.github.com/samsch/63a54e868d7fa2b6023a |
This is sensible. You still want to retain some human readable part in the action name, but generating unique prefixes totally works. |
If you can prove that they are unique, of course. |
Namespacing with convention has its perils when you are writing modules than can be used in various places that is outside your control. Lets say you have a module "Geometry", with ActionType of "Area". This module is used in two places:
Now you have two conflicting namespace, depending on where your module gets used.
|
I am experimenting with the following pattern: create a dir for each type of collection that contains:
create consts in following format: // song-store/song-store-consts.js
export const ADD = 'SONG_STORE.ADD'
export const REMOVE = 'SONG_STORE.REMOVE' When using constants in reducers, saga or actions // song-store/song-store-actions.js
import * as SONG_STORE from './song-store-consts'
export function addSongStore(name) {
return {
type: SONG_STORE.ADD,
name
}
}
export function removeSongStore(songStoreId) {
return {
type: SONG_STORE.REMOVE,
songStoreId
}
} Sadly not great for tree shaking. Would be nice if ES6 allowed: import { ADD, REMOVE } as SONG_STORE from './song-store-actions' Anyone know if Webpack 2 can intelligently tree shake |
This seems to be a very common issue and it pops up around our office often, either:
We tried a few different solutions but nothing seemed to stick:
After experimenting for the last few months, we decided to use the directory structure to namespace our action types. Typing these out manually gets old really fast, so I attempted to do something with ExampleIn
Feel free to give it a try, I hope it's useful. It would be fantastic to get any feedback, especially from @pbc or @gaearon as the issue creator and library creator. |
I was really hoping someone would offer up a "standard". We don't need symbols or objects for this, we just need a convention. Are we going to do |
I think with the dearth of other available conventions, Ducks has become the defacto standard.
|
@gaearon has always mentioned that 1:1 relation between actions and reducers is probably a bad idea and I really do agree. But what about the case where two different views actually want to call the same reducer. <Edit 1> Anyways, would love to hear from the experts. Thanks. |
@ivks : several thoughts here. First, you can dispatch the same action from multiple places within the application. Second, you can definitely have the same reducer listening for multiple action types, and handling them the same way. Third, the "1:1 relation" aspect is about being able to have multiple slice reducers listening to the same action, with each one updating its bit of state independently. Yes, much of the time there might only be one reducer that cares about a given action, but having multiple reducers respond is very much an intended use case for Redux. |
I actually was using redux-actions, and did not know it has a utility method combineAction which will do the task. Just found it and your above statement just mention the same. (should have been more clear, sorry) |
I don't think Ducks defines correctly in ActionTypes:
The reason is one action may be subscribed by more than one reducers. As @mherodev said, |
Why not just use a globally self-incremental integer to identify the action types? e.g.
|
Thought about this a bit, to make sure we don't clash with other developers, we're writing a lint rule, which looks into a directory (where we've got all reducers and each reducers have their own action types as constants) We're planning on linting such that it reports a violation if there are two constants with same value. If there's any suggestion, please let me know :) |
I have spent about a week now trying to find any good implementations of action type namespacing and have rolled my own which is based on Transition classes which hold definition of an action creator, reducer and a list of it's child transitions, but this approach requires way too much boiler plate code and it's problematic when you have a lot of very simple actions.
In all of the examples which you can find in this repo and plenty of others this problem is always ignored for some reason but this was the first problem I have spotted when started looking into Redux.
Am I missing something here ? You guys have no action type naming collisions ?
Can someone update the examples and show me how you deal with this problem or point me in the right direction ?
My specific case is related to having 2 separate admin panels on the frontend. One for testers and one for customers which will have their state stored in separate sections of the store ("testerAccount", "customerAccount"), but both of them will be able to do similar things for e.g. ADD_VIDEO_UPLOAD, ADD_COMMENT, etc, etc.
I would really appreciate your help here :)
The text was updated successfully, but these errors were encountered: