I'm trying to get how to add ngrx into my current project structure.
I've got all concepts around ngrx/reflux. Nevertheless, I don't quite figure out how to rebuild project structure in order to integrate it into my project.
Where do I need to add reducers?
Where do I need to add states?
What about actions?
Where should Store be, on a service, injected in each component?
Where or when the data should be fetched from server?
Is there any project structure best practice over there?
First, you should take a look into #ngrx/store documentation : https://github.com/ngrx/store#setup
I've made a small (funny) project to demonstrate how to use :
- angular
- ngrx/store
- ngrx/effects
- normalized state
- selectors
You can find it here : https://github.com/maxime1992/pizza-sync
To give you some info about how it works :
- core.module.ts is were I declare my root reducer
- root.reducer.ts is were I build the root reducer and compose it with middlewares according to dev/prod env
- For a reducer, I keep every related part together (interface(s) + reducer + effects + selectors)
Then within a component, to access the store simply do like that :
Inject the store :
constructor(private _store$: Store<IStore>) { }
Get data from either
a) A selector (ex)
this._pizzasCategories$ = this._store$.let(getCategoriesAndPizzas());
b) Directly from the store (ex)
this._idCurrentUser$ = this
._store$
.select(state => state.users.idCurrentUser);
Notice that I didn't subscribe which means I'm using in my view the async pipe so angular subscribe to the Observable for me.
But of course you can also do it by hand and use subscribe in your ts.
PS: I'll be releasing a starter with all that already setup to avoid loosing time with all of that when starting a new project. I'll try to release it this week and I'll update this post as soon as it's done. Maybe it might help you.
EDIT 12/05/17
Late but I finally released the starter :) !
https://github.com/maxime1992/angular-ngrx-starter
Related
Hello this is my first question. I am trying to set up a project where modules along with the redux and sagas will be injected into the main app, using redux-injectors. In my sagas I want to use yield select, to check if an action has updated the state and then carry on. For example, when I post an image, I want to make sure there were no errors in posting the file and then move on. I use the following function:
export const imageErrors = (state: RootState): IImagesErrorState => state.image.errors
and then in the saga.ts file I use it as such:
if (imagesErrors?.postImageError !== null) {
throw imagesErrors.postImageError
}
this works fine as long as the state.image exists in the root state from the beginning. However, how do I do that when I want to inject this state later on using useInjectReducer and useInjectSaga? I obviously get an error
Property 'image' does not exist on type 'Reducer<CombinedState<{ user: CombinedState<{ auth: IAuthState; errors: IErrorState; }>; }>, AnyAction>'.ts(2339)
So how do we handle selectors of specific pieces of state, since state does not yet include them?
Thank you so much.
Can't talk about the Typescript part of things, but in terms of architecture you've got two options.
One is the obvious - that is to add conditions or ? everywhere to avoid errors from accessing missing properties, but that can get tedious quickly.
The other probably better option is to rethink your state & application chunks. What is this saga that is accessing state that isn't existing yet? Does it need to run before you have such state? If not, let's move the saga to the same chunk as the reducer. In the opposite case, where you need the saga to be running e.g. as part of the runtime chunk, then perhaps the image state should be in the runtime chunk as well.
We working on a new project with a stack of Node.js in the BE + React and Redux in the FE.
Basically, this project is a "thin" version of an existing project - so it will have some basic features of the existing project + some new features.
I am looking for a way to reduce the copy-paste and duplication of code in those 2 projects(and can be more "duplicate" projects like this in the future).
In the BE we don't have any problem - the BE is the same for all FE projects.
On the other side, we have a UI library that we use - so all the atomic components are reused and we do not have any "copy-paste" here too.
The parts that we found ourselves copy-paste between multiple projects are all the "Redux" part - reducers, selectors, actions, etc.
There are any best practices/libraries/design patterns that we can follow to write the "Redux" parts once and use them in multiple projects?
The way I implement this is to have reusable logic hold its state as a separate branch in the store. So for example:
const state = {
user: defaultUserState,
messages: defaultMessagingState,
search: defaultSearchState
}
The reducers are similarly implemented in separate sub-components and combined:
combineReducers({
user: userReducer,
messages: messagingReducer,
search: searchReducer
})
I use redux-logic which is implemented as middleware. I am expecting to be able to do the same thing with the side effects.
The current leakage is related to navigation - which feature components are shown depending on which actions have been fired is defined in the main next project in the top level navigation reducer.
I recently refactored half my app to use the provider pattern and i now have a problem. The main issue is that i need to initialise controllers in the init (e.g. a text controller to have an initial value or the list size of a tab controller)
How am i meant to init Controllers if the data i need has to come from the state in the build method.
For example.
// This must go in the build as it requires state
myTabsController = TabController(length: myState.list.length, vsync: this);
I'm initialisng the controller every time it builds now... How am i meant to put this in the init but still access the state variables (as there is no context).
I've tried using the afterFirstLayout() callback from the AfterLayoutMixin library but that just causes more problems. Currently with the tab bar it flashes error as no tab initialised for the first frame and then displays properly when the afterFirstLayout is called and initialises the tab. This seems like a hacky fix
I would like to learn more about how to use this pattern properly and what would be the best solution to this problem.
Feel free to ask me to clarify more.
Thanks for your help.
I have a dotnet core application.
My Startup.cs registers types/implementations in Autofac.
One of my registrations needs previous access to a service.
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterSettingsReaders(); // this makes available a ISettingsReader<string> that I can use to read my appsettings.json
containerBuilder.RegisterMyInfrastructureService(options =>
{
options.Username = "foo" //this should come from appsettings
});
containerBuilder.Populate(services);
var applicationContainer = containerBuilder.Build();
The dilemma is, by the time I have to .RegisterMyInfrastructureService I need to have available the ISettingsReader<string> that was registered just before (Autofac container hasn't been built yet).
I was reading about registering with callback to execute something after the autofac container has been built. So I could do something like this:
builder.RegisterBuildCallback(c =>
{
var stringReader = c.Resolve<ISettingsReader<string>>();
var usernameValue = stringReader.GetValue("Username");
//now I have my username "foo", but I want to continue registering things! Like the following:
containerBuilder.RegisterMyInfrastructureService(options =>
{
options.Username = usernameValue
});
//now what? again build?
});
but the problem is that after I want to use the service not to do something like starting a service or similar but to continue registering things that required the settings I am now able to provide.
Can I simply call again builder.Build() at the end of my callback so that the container is simply rebuilt without any issue? This seems a bit strange because the builder was already built (that's why the callback was executed).
What's the best way to deal with this dilemma with autofac?
UPDATE 1: I read that things like builder.Update() are now obsolete because containers should be immutable. Which confirms my suspicion that building a container, adding more registrations and building again is not a good practice.
In other words, I can understand that using a register build callback should not be used to register additional things. But then, the question remain: how to deal with these issues?
This discussion issue explains a lot including ways to work around having to update the container. I'll summarize here, but there is a lot of information in that issue that doesn't make sense to try and replicate all over.
Be familiar with all the ways you can register components and pass parameters. Don't forget about things like resolved parameters, modules that can dynamically put parameters in place, and so on.
Lambda registrations solve almost every one of these issues we've seen. If you need to register something that provides configuration and then, later, use that configuration as part of a different registration - lambdas will be huge.
Consider intermediate interfaces like creating an IUsernameProvider that is backed by ISettingsReader<string>. The IUsernameProvider could be the lambda (resolve some settings, read a particular one, etc.) and then the downstream components could take an IUsernameProvider directly.
These sorts of questions are hard to answer because there are a lot of ways to work around having to build/rebuild/re-rebuild the container if you take advantage of things like lambdas and parameters - there's no "best practice" because it always depends on your app and your needs.
Me, personally, I will usually start with the lambda approach.
I'm using Redux to write a NodeJS app. I'm interested in allowing users to dynamically load middleware by specifying it at runtime.
How do I dynamically update the middleware of a running Redux application to add or remove middleware?
Middleware is not some separate extension, it's part of what your store is. Swapping it at runtime could lead to inconsistencies. How do you reason about your actions if you don't know what middleware they'll be run through? (Keep in mind that middlewares don't have to operate synchronously.)
You could try a naive implementation like the following:
const middlewares = [];
const middlewareMiddleware = store => next => act => {
const nextMiddleware = remaining => action => remaining.length
? remaining[0](store)(nextMiddleware(remaining.slice(1)))(action)
: next(action);
nextMiddleware(middlewares)(act);
};
// ... now add/remove middlewares to/from the array at runtime as you wish
but take note of the middleware contract, particularly the next argument. Each middleware receives a "pass to the next middleware" function as part of its construction. Even if you apply middlewares dynamically, you still need to tell them how to pass their result to the next middleware in line. Now you're faced with a loss-loss choice:
action will go through all of the middleware registered at the time it was dispatched (as shown above), even if it was removed or other middleware was added in the meantime, or
each time the action is passed on, it goes to the next currently registered middleware (implementation is a trivial excercise), so it's possible for an action to go through a combination of middlewares that were never registered together at a single point in time.
It might be a good idea to avoid these problems alltogether by sticking to static middleware.
Use redux-dynamic-middlewares
https://github.com/pofigizm/redux-dynamic-middlewares
Attempting to change middleware on-the-fly would violate the principle of 'pure' actions and reducer functions, because it introduces side-effects. The resulting app will be difficult to unit-test.
Off the top of my head, it might be possible to create multiple stores (one for each possible middleware configuration), and use a parent store to provide the state switch between them. You'd move the data between the sub-stores when switching. Caveat: I've not seen this done, and there might be good reasons for not doing it.