Addtional parameter with #ngrx/entity - ngrx

I want to keep employee array and page loading status in store state. So my initial state will look like this
const initialState = {
isLoading: false,
employees: []
};
Now i want to use #ngrx/entity for employee instead of array. The documentation only show the demo for using entity with entire state.
How can i use entity for only one property rather than entire state?
If it's not possible what is the alternative for above scenario?

See the docs for an example:
import { EntityState, EntityAdapter, createEntityAdapter } from '#ngrx/entity';
export interface User {
id: string;
name: string;
}
export interface State extends EntityState<User> {
// additional entities state properties
selectedUserId: number;
}
export const adapter: EntityAdapter<User> = createEntityAdapter<User>();

Related

NgRX - Store Modelling

Im creating an interface to send notifications.
The form will have the following fields:
title
message
users <-- Autocomplete
Im not sure how to model the state for this behaviour.
I already have an store:
export interface NotificationState extends EntityState<NotificationModle> {
loading: boolean;
loaded: boolean;
total: number;
current_page: number;
per_page: number;
}
Should I add a new users properties to my state or create a new one?
What you have for NotificationModel is correct.
User is a separate entity and should have its own EntityState.

Actions in multiple slices in Redux toolkit

The Redux toolkit docs mention using actions (or rather action types) in multiple reducers
First, Redux action types are not meant to be exclusive to a single slice. Conceptually, each slice reducer "owns" its own piece of the Redux state, but it should be able to listen to any action type and update its state appropriately. For example, many different slices might want to respond to a "user logged out" action by clearing data or resetting back to initial state values. Keep that in mind as you design your state shape and create your slices.
But, “keeping that in mind”, what is the best way to achieve this, given that the toolkit puts the slice name at the start of each action type? And that you export a function from that slice and you call that single function to dispatch the action? What am I missing? Does this have to be done in some way that doesn’t use createSlice?
It looks like this is what extraReducers is for:
One of the key concepts of Redux is that each slice reducer "owns" its slice of state, and that many slice reducers can independently respond to the same action type. extraReducers allows createSlice to respond to other action types besides the types it has generated.
It is a little strange that the action dispatcher should know which reducer the action belongs. I'm not sure the motivation of having reducers and extraReducers, but you can use extraReducers to allow several slices to respond to the same action.
I've found that using the extraReducers functionality when creating a slice with createSlice is the best way to do it.
In my case I've implemented this by creating a 'SliceFactory' class for each related feature. I've used it to do exactly what is in the example and reset relevant slices on user logout by listening for a LOGOUT_USER action.
Reference:
extraReducers: https://redux-toolkit.js.org/api/createSlice#extrareducer
Original article I used for the factory: https://robkendal.co.uk/blog/2020-01-27-react-redux-components-apis-and-handler-utilities-part-two
import { createSlice } from '#reduxjs/toolkit';
import { LOGOUT_USER } from '../redux/actions';
class CrudReducerFactory {
constructor(slice, state = null, initialState = {}) {
state = state || slice;
this.initialState = initialState;
const reducerResult = createSlice({
name: slice,
initialState: initialState[state],
reducers: this._generateReducers(),
extraReducers: (builder) => {
builder.addCase(LOGOUT_USER, (state, action) => {
return { ...this.initialState };
});
},
});
this.reducer = reducerResult.reducer;
this.actions = reducerResult.actions;
}
_generateReducers = () => {
return {
// Create One
requestCreateOne: (state, action) => {
state.isLoading = true;
},
requestCreateOneSuccess: (state, action) => {
state.isLoading = false;
state.one = action.payload;
},
requestCreateOneError: (state, action) => {
state.isLoading = false;
},
// ...snip...
};
};
}
export default CrudReducerFactory;
This is instantiated like so:
const factory = new CrudReducerFactory('users', 'users', { foo: 'bah', one: null, isLoading: false } );
The first argument is the name of the slice, the second is the slice of state and the third is the initial state.
You can then use factory.reducer and factory.actions to use accordingly.

Vuex selector like redux reselect

In my nuxt project, I have props that can be deduced from other store attributes.
In redux, I would use reselect to compute those every time it is necessary, when the store is mutated, and not necessarily at every render?
How can I achieve the same with nuxt/vuex?
Thanks
I think what you're after are getters.
Vuex allows us to define "getters" in the store. You can think of them as computed properties for stores. Like computed properties, a getter's result is cached based on its dependencies, and will only re-evaluate when some of its dependencies have changed.
// store/index.js
export const state = () => ({
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
})
export const getters = {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
mounted() {
// accessing in a component
this.$store.getters.doneTodos
}
Check out the documentation I linked, which has more examples, including how use method based getters, which don't cache the result.
If your getter is defined within a module then the the syntax is a little different. You need to do store.getters['module/doneTodos'], where module is the name of the store module you have.

Accessing a reducer state from within another reducer in NGRX

How can i access (read) the state of a reducer state within a another reducer in NGRX?
It is a very similar question to this.
Does NGRX offers any other solution to this?
I just stumbled upon this question when thinking about doing something similar. I needed a copy of some state information from a different reducer as a temporary "editing" data set that could be canceled and reverted back to the "recorded" data set.
I ended up extending my UI reducer (the reducer that held the state/edits for the current user's session) to include a property to hold a copy of data from my Data reducer (the reducer that represents what is stored in the database). That data gets passed in via a "Start" action.
Here is an abbreviated copy of my UI action:
import { Action } from "#ngrx/store";
import {
ICableFeature
} from "../../shared/models";
export enum UIActionTypes {
...
UI_EDIT_CUSTOM_TAGS_START = "[UI] Edit Custom Tags Start",
UI_EDIT_CUSTOM_TAGS_UPDATE = "[UI] Edit Custom Tags Update",
UI_EDIT_CUSTOM_TAGS_SAVE = "[UI] Edit Custom Tags Save",
UI_EDIT_CUSTOM_TAGS_SUCCESSFUL = "[UI] Edit Custom Tags Successful",
UI_EDIT_CUSTOM_TAGS_FAILED = "[UI] Edit Custom Tags Failed",
UI_EDIT_CUSTOM_TAGS_CANCELED = "[UI] Edit Custom Tags Canceled"
}
...
export class EditCustomTagsStart implements Action {
readonly type = UIActionTypes.UI_EDIT_CUSTOM_TAGS_START;
constructor(public payload: ICableFeature) {}
}
export class EditCustomTagsUpdate implements Action {
readonly type = UIActionTypes.UI_EDIT_CUSTOM_TAGS_UPDATE;
constructor(public payload: ICableFeature) {} // This payload has a copy of the data I needed from the other reducer. Make sure it is a copy and not the same object!
}
export class EditCustomTagsSave implements Action {
readonly type = UIActionTypes.UI_EDIT_CUSTOM_TAGS_SAVE;
constructor() {}
}
export class EditCustomTagsSuccessful implements Action {
readonly type = UIActionTypes.UI_EDIT_CUSTOM_TAGS_SUCCESSFUL;
constructor() {}
}
export class EditCustomTagsFailed implements Action {
readonly type = UIActionTypes.UI_EDIT_CUSTOM_TAGS_FAILED;
constructor(public payload: string) {}
}
export class EditCustomTagsCanceled implements Action {
readonly type = UIActionTypes.UI_EDIT_CUSTOM_TAGS_CANCELED;
constructor() {}
}
export type UIActions =
...
| EditCustomTagsStart
| EditCustomTagsUpdate
| EditCustomTagsSuccessful
| EditCustomTagsFailed
| EditCustomTagsCanceled;
And here is an abbreviated copy of my UI reducer:
import * as fromUI from "../actions/ui.actions";
import {
IOrchestratorState,
IOrchestratorStatusState,
ICableFeature
} from "../../shared/models";
export const uiFeatureKey = "ui";
export interface State {
showScroll: boolean;
fillingGaps: boolean;
fillGapStatus: IOrchestratorStatusState;
currentFillGapState: IOrchestratorState;
editingCustomTags: boolean;
customTagEdits: ICableFeature;
}
export const initialState: State = {
showScroll: true,
fillingGaps: false,
fillGapStatus: null,
currentFillGapState: null,
editingCustomTags: false,
customTagEdits: null
};
export function reducer(state = initialState, action: fromUI.UIActions) {
switch (action.type) {
...
case fromUI.UIActionTypes.UI_EDIT_CUSTOM_TAGS_START:
return {
...state,
editingCustomTags: true,
customTagEdits: action.payload // This is a copy of the data I needed from the other reducer
};
case fromUI.UIActionTypes.UI_EDIT_CUSTOM_TAGS_UPDATE:
return {
...state,
customTagEdits: action.payload // This is the updated information from user edits, not saved.
// I can also create a router guard that checks to makes sure the data in this
// property and the data in my Data store are the same before the page is deactivated
};
case fromUI.UIActionTypes.UI_EDIT_CUSTOM_TAGS_SUCCESSFUL:
case fromUI.UIActionTypes.UI_EDIT_CUSTOM_TAGS_FAILED:
case fromUI.UIActionTypes.UI_EDIT_CUSTOM_TAGS_CANCELED:
return {
...state,
editingCustomTags: false,
customTagEdits: null
};
default:
return state;
}
}
As you can see, by passing the data from one reducer to the other as payloads in actions, I am able to maintain pure functions in my reducers.
I hope this helps!

Redux Newbie Trying to Make Pure-Data Class That Uses Redux Store

Basic Problem
I've got a Redux store with the following data:
foo: {
currentId: 1,
things: [{id: 1}, {id: 2}),
}
I'd like to make a utility method somewhere (eg. on a Foo singleton object) such that any module in my code can do:
import Foo from 'foo';
foo.getCurrentFoo(); // returns foo.thins[foo.currentId];
but I'm having trouble figuring out where to put it.
Failed Attempt
My initial attempt was to create a Foo component singleton:
// Foo.js
class FooBase extends React.Component {
getCurrentFoo() {
return this.state.foo.things[this.state.foo.currentId];
}
}
const Foor = connect((state) => state.foo)(FooBase);
export default new FooWrapper();
But that doesn't work. Redux complaieds about the property store not existing (when I did new FooWrapper()). That makes sense, because my component isn't inside a <Provider />. However, I just want a stand-alone utility class/object, not something actually in the DOM, which rules out <Provider/>.
How can I make a method like the one described above, that actually works, without involving <Provider /> ... and where do I put it?
The nice thing about the react-redux helpers is that they allow you to use connect() and <Provider /> to automatically pass the store to child components via React's context. However, that doesn't necessarily mean that you have to use these helpers, especially in areas of a codebase that don't use React.
So here lies the problem: connect() and <Provider /> help us by giving our React components access to a singleton instance of a store, but how can we access this store somewhere where connect() and <Provider /> can't be used?
I think the easiest solution here is to create a singleton class that holds on to the store, so any non-React module can still use the store.
So let's say you're creating your store like this:
init.js
import {createStore} from 'redux';
const initialState = {
currentId: 1,
things: ['foo', 'bar']
};
const reducer = (state = initialState, action) => {
if (action.type === 'SET_CURRENT_ID') {
return Object.assign({}, state, {
currentId: action.id
});
}
return state;
};
const store = createStore(reducer);
This store takes an action of type SET_CURRENT_ID which simply returns a new state with the currentId property changed to whatever was handed to it. You could then get the current "thing" by doing something like store.getState().things[store.getState().currentId]. So let's create a Singleton class that can hold on to the store and provide a wrapper around this functionality.
store.js
class Store {
constructor() {
this._store = undefined;
}
setStore(store) {
this._store = store;
}
getCurrentThing() {
if (this._store) {
const {things, currentId} = this._store.getState();
return things[currentId];
}
}
setCurrentThing(id) {
if (this._store) {
const action = {
type: 'SET_CURRENT_ID',
id
};
this._store.dispatch(action);
}
}
}
export let singletonStore = new Store();
This class creates an instance the first time it is used, and uses that instance every subsequent time. So when you originally create your store, simply import this class and call setStore().
init.js
import {singletonStore} from './store';
singletonStore.setStore(store);
Then, every subsequent file where singletonStore is used will have the same state.
test.js
import {singletonStore} from './store';
console.log(singletonStore.getCurrentThing()); // 'bar'
singletonStore.setCurrentThing(0);
console.log(singletonStore.getCurrentThing()); // 'foo'
This should work just fine for your need to use your store in modules that don't have the benefit of being passed the store magically with connect() and <Provider />.

Resources