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

Anti-pattern: reacting to @@INIT action #186

Closed
gaearon opened this issue Jun 28, 2015 · 18 comments

Comments

@gaearon
Copy link
Contributor

commented Jun 28, 2015

In the new docs (#140) we should clarify that any actions prefixed with @@ are not meant to be handled. For example, you should never try to handle @@INIT. We might enforce that by slightly randomizing names (e.g. @@INIT_2hj3jh34).

Handling @@INIT manually will break hot reloading. It is invoked at every hot reload, so if you do your initial data transformation there, it won't work the second time.

I've seen people using @@INIT to hydrate the plain objects received from the server into the Immutable state. Instead of handling @@INIT, you need to consider one of two options:

  • convert your whole plain object state to Immutable right away before passing it to Redux;
  • check if the state is a plain object and use fromJS before handling the action.
function myReducer(state = {}, action) {
  if (!isImmutable(state)) { // some kind of typeof check here?
    state = Immutable.fromJS(state);
  }

  ...
}

It is easy to abstract this away with a createImmutableReducer function like this:

export default function createImmutableReducer(initialState, handlers) {
  return (state = initialState, action) => {
    if (!isImmutable(state)) {
      // Hydrate server plain object state
      state = Immutable.fromJS(state);
    }

    const handler = handlers[action.type];
    if (handler) {
      state = handler(state, action);
    }

    if (!isImmutable(state)) {
      throw new Error('Reducers must return Immutable objects.');
    }

    return state;
  };
}

It can then be used for creating reducers from action constant -> handler map.

@gaearon gaearon added the docs label Jun 28, 2015

@Markus-ipse

This comment has been minimized.

Copy link

commented Jul 1, 2015

@gaearon What is "@@"? I'm assuming it's ES6, perhaps related to symbols, but google doesn't like special characters so it ungoogleable :(

@dariocravero

This comment has been minimized.

Copy link
Contributor

commented Jul 1, 2015

@hummlas it's random to tell it's an internal :) @ooflorent proposed it as far as I remember

@Markus-ipse

This comment has been minimized.

Copy link

commented Jul 1, 2015

Ah, I see, like old-school underscore prefixes, e.g. var _privateValue = "Private";? :)

It seem to be used in official spec-stuff as well, like iterables

"In order to be iterable, an object must implement the @@iterator method, meaning that the object (or one of the objects up its prototype chain) must have a property with a Symbol.iterator key"

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols

This confuses me a bit as you're supposed to implement an @@iterator method, which, to me, seems to be at odds with @@ meaning "internal"?

@gaearon

This comment has been minimized.

Copy link
Contributor Author

commented Jul 1, 2015

Feel free to suggest better prefix. I have no idea what to choose to discourage people from actually handling them, but keep it readable. Maybe prefix with an emoji..

@clearjs

This comment has been minimized.

Copy link
Contributor

commented Jul 1, 2015

I'd use something like @@redux/INIT, or @@redux/__INIT to stress that it's private. This would be similar to, e.g. @@transducer/step in that there is a namespace at the top.

@gaearon

This comment has been minimized.

Copy link
Contributor Author

commented Jul 1, 2015

Oh, I like the namespace thing!

@dariocravero

This comment has been minimized.

Copy link
Contributor

commented Jul 1, 2015

REDUX:INIT?

@clearjs

This comment has been minimized.

Copy link
Contributor

commented Jul 1, 2015

I like the @@ because of possible familiarity (it is already used for iterators and transducers in a similar manner). Capitalization is to look like an action constant. Underscores as a hint for internals. Making it look frightening as an additional hint :) It would also be extensible for possible non-privately namespaced constants.

But any naming scheme should be fine, provided that it is unlikely to clash with user's action constants.

@Markus-ipse

This comment has been minimized.

Copy link

commented Jul 2, 2015

Could symbols be used for methods and variables that aren't supposed to be handled by the consumer?

But maybe that would break the hot reloading as well? But then if that's the case that could be solved by exposing a collection of symbols, that way the methods/variables wouldn't be accessible without retrieving there corresponding symbol first?

I might be speaking out of ignorance here though as I have no experience with hot reloading or symbols (yet), so I defer to your superior knowledge in this area :)

Btw, I must say that reading the issues for Redux is extremely educational and enjoyable, it's so cool being able to follow along in the creation of a new library, especially the back and fourth between @gaearon and @acdlite when brainstoring new ideas. Kudos! :)

@clearjs

This comment has been minimized.

Copy link
Contributor

commented Jul 2, 2015

Symbols might be problematic for use from third-party libraries: they would be required to import redux, which may lead to more coupling.

@gaearon

This comment has been minimized.

Copy link
Contributor Author

commented Jul 6, 2015

Could symbols be used for methods and variables that aren't supposed to be handled by the consumer?

That's what I wanted at first, but we decided against them because it feels silly to drag a Symbol polyfill (16% of the lib size) just for that.

@acdlite

This comment has been minimized.

Copy link
Collaborator

commented Jul 6, 2015

Maybe we should namespace the init type, like the transducer protocol does?

export const INIT = '@@redux/init';
@acdlite

This comment has been minimized.

Copy link
Collaborator

commented Jul 6, 2015

Oh haha that was already suggested. Well I like that idea :D

gaearon added a commit that referenced this issue Jul 13, 2015

gaearon added a commit that referenced this issue Jul 13, 2015

Merge pull request #259 from gaearon/forbid-handling-private-actions
Handling private actions is an anti-pattern. Enforce it. (Fixes #186)
@gaearon

This comment has been minimized.

Copy link
Contributor Author

commented Jul 13, 2015

Fixed by #186.

@aravantv

This comment has been minimized.

Copy link

commented Mar 21, 2016

Just for info, I arrived here precisely because I was feeling it would be bad to react to this action, so I think the name makes it actually pretty clear it should not be used. If you really want to make it clear that it should not be used I think the best name would be "@@INIT_action_do_not_react_to_this_action_check_issue_186_instead" or something alike...

@ghost

This comment has been minimized.

Copy link

commented Dec 9, 2016

So I am in a situation where I would like to call a saga on startup. Is there any better way then listening to @@INIT ?

@brigand

This comment has been minimized.

Copy link
Contributor

commented Dec 9, 2016

@MoeSattler const store = createStore(...); store.dispatch(...)

rart added a commit to craftercms/studio-ui-ng that referenced this issue Dec 17, 2017

* Completely removing handling of REDUX_INIT action (reduxjs/redux#186)…
… in favour of just STUDIO_INIT. This fixed a bug where chrome was getting a different @@init than safari causing erratic behaviour in safari

@markkopaas markkopaas referenced this issue Jan 9, 2019

Merged

Feature/ois/result tabs info #1147

0 of 2 tasks complete
@Xesenix

This comment was marked as resolved.

Copy link

commented Apr 29, 2019

So whats the purpose to having init action that i cannot use to init values. Also it is double confusing when someone uses redux dev tools where it is just @@INIT and when using compose on production build he will learn that its not working after wasting some time on debugging and then finding this thread...

And to fix my issues I need to send similar action just after creating store...

const defaultState = {
  iDontWantDeepState: true,
}
const reducer = (state = {}, action) => {
  switch (action.type) {
    case MY_INITIALIZE_BECAUSE_WHY_HAVE_STANDARDS:
      return { ...defaultState, ...state };
  }
}

@reduxjs reduxjs locked as resolved and limited conversation to collaborators Apr 29, 2019

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
8 participants
You can’t perform that action at this time.