Platform: Accept initialState function to allow dynamic initial state in AoT

Created on 30 Jun 2017  路  13Comments  路  Source: ngrx/platform

This is problem carried over from @ngrx/store (see: 273, 348).

AoT requires static objects in module definitions, so dynamic initial state is only possible with:

  1. At least support for an initialState function
    or
  2. Putting a meta-reducer on top of everything for this purpose alone (as described in my comment on 348). The second case seems unnecessarily unnecessarily complicated for something so easy to support in the library itself.
Store enhancement help wanted

Most helpful comment

You can also provide your own initial state through the INITIAL_STATE token.

import { INITIAL_STATE } from '@ngrx/store';

// in the providers array
{ provide: INITIAL_STATE, useFactory: getInitialState }

All 13 comments

Seems legit. Want to take a crack at making a PR?

You can also provide your own initial state through the INITIAL_STATE token.

import { INITIAL_STATE } from '@ngrx/store';

// in the providers array
{ provide: INITIAL_STATE, useFactory: getInitialState }

@brandonroberts That's a great idea, and I'm a bit embarrassed I didn't think to do it.

That said, allowing initialStateFactory to take a function is really trivial, and considering it's necessary for AoT, it might be worthwhile to add support for it.

I'm happy to open a PR, but I'm wondering your thoughts. Either a PR documenting how to override the provider (since you're exporting all the tokens), or a PR allowing initialState to be a function.

Thanks

@bfricka a PR allowing initialState to be a function along with documentation would be good.

@brandonroberts @robwormald This PR should be ready to. It looks like there's perhaps an issue with the COVERALLS_REPO_TOKEN environment variable not being set in CircleCI. You can add it to the circle project without committing it to git (Project settings > Environment Variables).

@bfricka Could you explain how to use initialStateFactory to dynamically get the initial state from the backend?

This may seem like an odd question, but do you need initial state before the application is loaded? Like, are you trying to re-hydrate state saved on the backend? Because there is an inherent problem with this approach if so: You'd need to delay instantiation of your application until you successfully retrieved the initial state. Otherwise, it's just updating state.

Providing initial state implies that you have access to it, and if you don't you probably shouldn't hold up application bootstrap specifically for that purpose (see "Shell Architecture").

All this said, a standard way of providing initial state is described now in the docs.

Just FYI, the use case I applied this to was retrieving initial state via WebView#addJavaScriptInterface on Android.

@bfricka in the official docs, the way the example get the initial state, the const initialStateFromSomewhere its supposed to be "dynamically injected at runtime". Could you care to detail how that could be achieved? I know probably this is not ngrx business exactly but it probably would help a lot to lost ppl like me :/

@Dunos Yeah, so intialStateFromSomewhere means a state object (probably partial slice) that is available at runtime and is not statically defined in the code. Somewhere could be from a number of synchronous sources:

  • Provided in the HTML from the server (global)
  • Re-hydrated from Web Storage (e.g. localStorage)
  • Loaded via synchronous XHR (e.g. YAML.load, don't do this)

Ideally, this could be an asynchronous function, but @ngrx is synchronous.

Just to re-iterate what @Dunos said. I think an official doc, stating how initialStateFromSomewhere would be achieved would be terrific. I just created the following feature request https://github.com/ngrx/platform/issues/770, and part of me says that I might already be able to do this.

Apologies if I'm missing something glaringly obvious but what if I wanted to call a service from within the getInitialState() function? I have an Angular Service that reads a local JSON file and I want to dynamically set a config item stored in the store on load. Is something like that possible?

// Static state
const initialState = { counter: 2 };

// In this function dynamic state slices, if they exist, will overwrite static state at runtime.
export function getInitialState() {

// what if you wanted to call an Angular service from here to populate the "dynamicVar" variable?
  return { ...initialState, ...dynamicVar };
}

@NgModule({
  imports: [
    StoreModule.forRoot(reducers, { initialState: getInitialState })
  ]
})

EDIT - I'm guessing I'd have to do the following (unless I'm missing something) - https://stackoverflow.com/a/41211624

I also want to use a service to get/set some initial state. Is there plan to allow injecting service inside reducers? Or do we just manually initialize a service and use it in the reducer? For now, I am manually dispatching some action with payload to update the state during component initialization.

@maxisam @VinodhSubramanian1193 @bfricka this is little bit to late but maybe it can help you and others when loading from localstore/indexeddb respectively sync/async into ngrx/store dynamic => https://medium.com/whozapp/use-indexeddb-with-ngrx-for-fast-angular-bootstrap-d9900f0cbc1b

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hccampos picture hccampos  路  3Comments

NathanWalker picture NathanWalker  路  3Comments

brandonroberts picture brandonroberts  路  3Comments

mappedinn picture mappedinn  路  3Comments

bhaidar picture bhaidar  路  3Comments