Skip to content

[Feature Request] Option for action constants created by createSlice to follow the SCREAMING SNAKE CASE #898

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

Closed
jehillert opened this issue Feb 17, 2021 · 10 comments

Comments

@jehillert
Copy link

REQUEST:

If feasible, I would request an option whereby createSlice automatically generates uppercase action constants from the cameCase reducer names defined in the slice. Additionally, the "actions" property of the slice returned, instead of looking like this:

mySlice.actions = { myAction1, myAction2, myAction3 };

would look like this:

mySlice.actions = {
    MY_ACTION_1: 'MY_ACTION_1',
    MY_ACTION_2: 'MY_ACTION_2',
    MY_ACTION_3: 'MY_ACTION_3',
}

REASON FOR REQUEST:

The documentation for createSlice states:

The original Redux docs and examples generally used a "SCREAMING_SNAKE_CASE" convention for defining action types... The downside is that the uppercase strings can be hard to read... Redux Toolkit's createSlice function currently generates action types that look like "domain/action", such as "todos/addTodo". Going forward, we suggest using the "domain/action" convention for readability.

I disagree. I strongly prefer my snakes screaming over quiet and camel-like, precisely because I find it easier to read. It is much easier to distinguish particular actions from others in the right side of Redux Devtools, and I find I can instantly spot where an action is being called in a thunk, in the UI code, or in a library like redux-saga, because uppercase is only used in UI code, for the most part, to define display text.

Maybe the other reason I find it so easy to distinguish actions in all caps is it forces each action constant to form a distinct pattern of alternating solid and empty spaces. Hopefully the next few lines will illustrate.

More concentration to distinguish:

I am writing stuff all around so setAllGophersRabid will blend in.
I am writing stuff all around so retrieveMyHamburgers will blend in.

Less Concentration to distinguish:

I am writing stuff all around so SET_ALL_GOPHERS_RABID  will blend in.
I am writing stuff all around so RETRIEVE_MY_HAMBURGERS  will blend in.

The shape helps because more distinct

I am writing stuff all around so ▓▓_▓▓_▓▓▓▓▓▓▓_▓▓▓▓  will blend in.
I am writing stuff all around so ▓▓▓▓▓▓_▓▓_▓▓▓▓▓▓▓▓▓▓  will blend in.
@markerikson
Copy link
Collaborator

markerikson commented Feb 17, 2021

One of the major points about RTK is to be opinionated. We now specifically recommend using a "domain/eventName" pattern instead of single SCREAMING_SNAKE_CASE, and RTK implements that pattern:

https://redux.js.org/style-guide/style-guide#write-action-types-as-domaineventname

So no, we're not going to alter this behavior or make it customizable, same as we're not going to remove Immer.

Strictly speaking, you can get some of this yourself if you actually name the reducers that way:

const todosSlice = createSlice({
  name: "TODOS",
  initialState: [],
  reducers: {
    ADD_TODO(state, action) {}
  }
})

const {ADD_TODO} = todosSlice.reducers;
console.log(ADD_TODO("Buy Milk"))
// {type: "TODOS/ADD_TODO", payload: "Buy milk"}

But tbh that just seems silly.

@jehillert
Copy link
Author

jehillert commented Feb 17, 2021

Immer? I do not recall asking about that, or even know what it is really. Let me clarify something. I was asking because I thought it would be helpful for those easily mentally fatigued such as myself. I thought, "eh, they will probably say no. Maybe 20% chance they say 'we will think about it'". What is my point? My point is this was a feature "request", one step above a wishlist. It was not meant as an indictment of decisions your team has made with with respect to their very very wonderful code.

But... looking above, I see my writing is a bit "assertive" in tone. That has less to do with how I was feeling when I wrote the request and more to do with the fact that I was an attorney for 10 years before I switched to tech two years ago. Force of habit. Sorry.

@markerikson
Copy link
Collaborator

Immer is the library that lets you write "mutating" immutable update logic ( https://immerjs.github.io/immer/docs/introduction , https://redux.js.org/tutorials/fundamentals/part-8-modern-redux#immutable-updates-with-immer ).

The comparison I was making was that we've had some issues asking us to make use of Immer optional in createReducer and createSlice (ref: #242 ). My answer to that was "no, RTK is opinionated, and one of those strongly held opinions is use of Immer as standard behavior, so we're not changing that".

So, I was using that as a similar answer here: "no, RTK is opinionated, and one of those strongly held opinions is how we construct action type strings, so we're not changing that".

@jehillert
Copy link
Author

jehillert commented Feb 17, 2021

Ah, that's right. I remember the docs saying we can mutate state inside createSlice(). Look man, anybody who writes code that gets downloaded a couple million times a week is somewhat of a celebrity in my book. The value it provides to the world just blows my mind. So I'm plenty happy with you guys having an opinion about your own work. Not all requests are criticism, and in this case, all caps used to be your own standard. So if anyone needs a talking to, it is your past selves, for having an opinion people got cozy with, lol.

@phryneas
Copy link
Member

phryneas commented Feb 17, 2021

Redux never had any standard or best practices until the style guide and redux toolkit were released.

Just examples. And these examples were copied over and over without thinking and made it into thousands of tutorials. There was never an intention that for every reducer, there should also be an action file and for every action file there should also be a constants file. That was just an example and people started to do that stuff religiously without thinking if it made any sense for their own project, no matter how much it hurt.
All that because it seemed to make sense in a todo-list-example with 2 reducers and 7 actions.

As for this use case, I'll add my two cents: aside from the devtools, you should never lay your eyes on an action type when using RTK, nor reference it in your code. It is purely an implementation detail that should be completely hidden from the dev's eyes.
You should use action creators (and those have been camelCased even in all old examples ;) )

In your sagas, you should take with action creators as the first argument and you should put with action creators - as this way, the TypeScript support is greatly increased and even as a non-TypeScript user, your IDE will give you better feedback (even more when using typed-redux-saga).
You should match in hand-written logic using mySlice.actions.myAction.match(action) and not by comparing string types.
You should never ever need to use mySlice.actions.myAction.type as that, as well, is just an implementation detail.

As for the devTools: using slash-scoped lowerCamelCase allows for the distinction between whitespace (lower letter followed by uppercase letter) and domain separators (/) - which is just not possible in snake case, as both are replaced with an underscore there. So the level of information you gain from the opinionated way of RTK is actually higher.

@markerikson
Copy link
Collaborator

Heh, yeah. I think I remember Dan once saying that he "didn't think everyone would just copy the stuff he was showing in the docs" or something like that. Which, tbh, is kind of silly - the whole point of examples in docs is to give people patterns they can follow.

There were perfectly good reasons for using const ADD_TODO = "ADD_TODO", of course. SCREAMING_SNAKE_CASE is a standard idiom across languages for writing constant values, and it avoided writing the same string in different places and getting it wrong. Mostly.

But as Lenz said, today action types are mostly an implementation detail, and the only time you should really be seeing them is in the DevTools.

@jehillert
Copy link
Author

jehillert commented Feb 17, 2021

It's funny you say that about never seeing the action constants, and also about following the docs too literally, because I've spent the last 4-5 days reading up on redux saga and just started using it to replace the thunks in a personal project. Part of why I asked about the constants was because the docs for both libraries did a pretty good job of steering me clear of exposing constants. In other words, I noticed they were gone, but the way saga/TK are written and explained, it didnt feel quite right to inckude them..

Before checking out saga, I tried to pick up RxJS and redux-observable. My project isn't complex but I need to be able to perform 4-5 asynchronous actions sequentially while taking in values from 3 different external sources along the way (React Native bridge, emissions from NativeEmitter, API call to smart lock company's backend). My thinking for starting with Rx was transferable skills (it is ported all over the place), so why not invest the time. I wasn't familiar with saga.

Anyways, I spent about three weeks trying to figure out how to use redux-observable and finally just gave up. I could only make it do what I didn't want to use it for. Then I gave saga a shot and wow what a difference. I went through most of the documentation over the weekend, and so far haven't really had much trouble getting started with the implementation.

It also seems like most of the things on my "still don't have a good way of doing this" list are covered by this library. Maybe I don't need so much setup for what I'm doing, but then again it's the big enterprise projects that pay my bills. Its not overkill if the extra practice helps me out at work. Anyways, thanks for the libraries. pretty great stuff to use. Sure beats reading and writing about patents all day.

Last point, since it's not to clear from what I say above (been up for a while), your points make sense. Consider me convinced. Thanks much for the feedback.

@jehillert
Copy link
Author

jehillert commented Jul 20, 2021

I had a few more months after filing this issue to work with redux-toolkit on a personal project before starting a new job that uses a different state management library, both experiences coming after a year on a big sloppy-in-a-good-way enterprise project that lasted a year and relied heavily on basic redux. All I can say, is for a library I dreaded three years ago when I switched careers, it is now the neatest thing I've come across. A puzzle piece fits perfectly into one puzzle, that's not too amazing. A puzzle piece fits into 5 million different puzzles perfectly, and half of those puzzles sorta naturally self-organize around that one piece, that's quite another...

@slutske22
Copy link

I too was missing the SCREAMING_SNAKE_CASE from the good-ole redux days. Here's my solution, probably one of the dumber and more pointless bits of code I've written but it gets the job done:

import { configureStore } from "@reduxjs/toolkit";
import { snakeCase } from "lodash";

const store = configureStore({
  reducer: { ...reducers here },
  devTools:
    process.env.NODE_ENV !== "development"
      ? false
      : {
          actionSanitizer: (action) => {
            const domain = action.type.split("/")[0];
            const actionType = action.type.split("/")[1];
            return {
              ...action,
              type: domain + "/" + snakeCase(actionType).toUpperCase(),
            };
          },
        },
});

@jehillert
Copy link
Author

jehillert commented Jan 27, 2024 via email

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

No branches or pull requests

4 participants