How to dispatch an action in NgRx Store DevTools with NgRx 8? - ngrx

I've updated my ngrx code to version 8 (with Action Creators etc.), but I don't know now how to use Dispatcher within NgRx Store DevTools.
Before I was able to dispatch actions like this:
{
type: '[Something] something loaded',
payload: {
results: ['a', 'b', 'c']
}
}

In my simple app I have the following Action:
export const SaveUserInfo = createAction(
'[User] Save user info',
props<{ user: IUser}>()
);
and the IUser model
export interface IUser {
name: string;
money: number;
}
Than in DevTools I dispatch like this:
{
user: {
name: 'coiso',
money: 1000
},
type: '[User] Save user info'
}
Hope it works for you.

Related

Can anyone help implementing Nuxt.js Google Tag Manager with function based id

I installed and add this code to my nuxt.config.js and it works perfectly fine. (Link to package)
modules: [
['#nuxtjs/google-tag-manager', { id: 'GTM-XXXXXXX' }],
]
Now I am trying to implement instead of a static ID a function which will return an ID.
I tried to add this lines into my nuxt.config. js but it is not working. Obviously I have to put it somewhere else or so...
This is what I tried
nuxt.config.js
const code = '1234567'
id: () => {
return 'GTM-' + code
}
export default {
...
modules: [
['#nuxtjs/google-tag-manager', { id: id }],
]
...
}
What would be the correct way implementing this?
I would like to do something like that at the end.
modules: [
['#nuxtjs/google-tag-manager', {
id: ({ req }) => {
if (req.headers.referer == "exmple.com")
return 'GTM-156'
if (req.headers.referer == "exmple.it")
return 'GTM-24424'
if (req.headers.referer == "exmple.es")
return 'GTM-2424'
}
}]]
EDIT:
I solved my problem by rewriting the whole module. It is not possible to use this Module because it is loaded only on build time. I rewrote the module and moved the code into nuxtServerInit.
nuxtServerInit is called on each request (modules only onetime). In the request I asked from which domain the request is coming. Depending on the domain I add different google-tag-manager id's to the head and the plugin.
From package docs:
modules: [
['#nuxtjs/google-tag-manager', {
id: () => {
return axios.get('http://example.com/')
.then(({ data }) => {
return data.gtm_id
})
}
}]]
You can use process.env.NODE_ENV inside function which will return an ID
Edit 1
To put the gtm id, depending on req.headers.referer you need to provide context to the function returning the id. This can be done in middleware
See how it works here
https://github.com/nuxt-community/modules/blob/master/packages/google-tag-manager/plugin.js
Edit 2
As far as I understand your question, it will not work to have a query context in the config.
Look at i18n middleware: request.locale - > store - > update modules (router, vuetify, moment, etc.)
https://nuxtjs.org/examples/i18n/
~/middleware/gtm.js
export default function ({ app, store }) {
// app.$gtm contains id, you can set another from store
}
don't forget to add middleware to the page
page.vue
export default {
middleware: ['gtm']
}

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.

Addtional parameter with #ngrx/entity

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>();

Ngrx: meta-reducers and capturing effects

I've added into my IStore a transaction concept. It straightforwardly stands for providing a way to store into my IStore which pending operations keep pending. When they are completed, they are removed:
export interface IStore {
user: IUser;
txs: ITxRedux;
}
All my reducers are like:
* reducer name: `'OPERATION'`
* success reducer name: `'OPERATION_SUCCESS'`
* failed reducer name: `'OPERATION_FAILED'`
Some of these reducers (only those need a http request) are captured using #Effects:
#Effect({ dispatch: true })
userLogin$: Observable<Action> = this._actions$
.ofType('USER_LOGIN')
.switchMap((action: Action) =>
{
....
});
Currently, my effects have this pattern:
return make_http_call
.map(_ => ({type: 'OPERATION_SUCCESS'}, payload: {...}))
.catch(_ => ({type: 'OPERATION_FAILED'}, payload: {...}));
So, I'd like to get a way by adding or removing a "transaction" into my IStore.txs each time an effect is called or completed. When I say "add a transaction into my IStore.txs" I mean to call transaction reducers:
public static ADD_TX = `ADD_TX`;
private static addTx(txsRdx: ITxRedux, type, payload: ITx) {
const tx = payload;
return {
ids: [ ...txsRdx.ids, tx.id ],
entities: Object.assign({}, txsRdx.entities, {[tx.id]: tx}),
};
}
public static REMOVE_TX = `REMOVE_TX`;
private static removeTx(txsRdx: ITxRedux, type, payload) {
const tx: ITx = payload;
var entitiesTmp = {...txsRdx.entities};
delete entitiesTmp[tx.id];
return {
ids: txsRdx.ids.filter(id => tx.id != id),
entities: entitiesTmp
};
}
I've listen to talk a bit about meta-reducers, but I don't quite whether they are going to be able to get my goal.
Is there any way to get it using a elegant way?
Late reply, but you might find this post useful. The classic example (taken mostly from that post) is to log each action/state change by means of a logging meta-reducer:
export function logging(reducer) {
return function loggingReducer(state, action) {
console.group(action.type);
// invoke following, "wrapped" reducer in the chain
const nextState = reducer(state, action);
console.log(`%c prev state`, `color: #9E9E9E`, state);
console.log(`%c action`, `color: #03A9F4`, action);
console.log(`%c next state`, `color: #4CAF50`, nextState);
console.groupEnd();
// return wrapped reducer output
return nextState;
};
}
In main app module, you compose the new logging reducer factory with the usual combineReducers reducer factory:
const appReducer = compose(logging, combineReducers)(reducers);
//...
StoreModule.provideStore(appReducer),
Just watchout for setting up StoreModule and global app reducer, as that syntax has changed in recent ngrx versions since that blog post.
On a side note, if you're looking for some inspiration on implementing a meta-reducer to catch and invoke remote API calls, you might want to have a look at an equivalent, already-made middleware for Redux, as redux-api-middleware. HTH

React and Redux: redirect after action

I develop a website with React/Redux and I use a thunk middleware to call my API. My problem concerns redirections after actions.
I really do not know how and where I can do the redirection: in my action, in the reducer, in my component, … ?
My action looks like this:
export function deleteItem(id) {
return {
[CALL_API]: {
endpoint: `item/${id}`,
method: 'DELETE',
types: [DELETE_ITEM_REQUEST, DELETE_ITEM_SUCCESS, DELETE_ITEM_FAILURE]
},
id
};
}
react-redux is already implemented on my website and I know that I can do as below, but I do not want to redirect the use if the request failed:
router.push('/items');
Thanks!
Definitely do not redirect from your reducers since they should be side effect free.
It looks like you're using api-redux-middleware, which I believe does not have a success/failure/completion callback, which I think would be a pretty useful feature for the library.
In this question from the middleware's repo, the repo owner suggests something like this:
// Assuming you are using react-router version < 4.0
import { browserHistory } from 'react-router';
export function deleteItem(id) {
return {
[CALL_API]: {
endpoint: `item/${id}`,
method: 'DELETE',
types: [
DELETE_ITEM_REQUEST,
{
type: DELETE_ITEM_SUCCESS,
payload: (action, state, res) => {
return res.json().then(json => {
browserHistory.push('/your-route');
return json;
});
},
},
DELETE_ITEM_FAILURE
]
},
id
}
};
I personally prefer to have a flag in my connected component's props that if true, would route to the page that I want. I would set up the componentWillReceiveProps like so:
componentWillReceiveProps(nextProps) {
if (nextProps.foo.isDeleted) {
this.props.router.push('/your-route');
}
}
The simplest solution
You can use react-router-dom version *5+ it is actually built on top of react-router core.
Usage:
You need to import useHistory hook from react-router-dom and directly pass it in your actions creator function call.
Import and Creating object
import { useHistory } from "react-router-dom";
const history = useHistory();
Action Call in Component:
dispatch(actionName(data, history));
Action Creator
Now, you can access history object as a function argument in action creator.
function actionName(data, history) {}
Usually the better practice is to redirect in the component like this:
render(){
if(requestFullfilled){
router.push('/item')
}
else{
return(
<MyComponent />
)
}
}
In the Redux scope must be used react-redux-router push action, instead of browserHistory.push
import { push } from 'react-router-redux'
store.dispatch(push('/your-route'))
I would love not to redirect but just change the state. You may just omit the result of deleted item id:
// dispatch an action after item is deleted
dispatch({ type: ITEM_DELETED, payload: id })
// reducer
case ITEM_DELETED:
return { items: state.items.filter((_, i) => i !== action.payload) }
Another way is to create a redirect function that takes in your redirect Url:
const redirect = redirectUrl => {
window.location = redirectUrl;
};
Then import it into your action after dispatch and return it;
return redirect('/the-url-you-want-to-redirect-to');
import { useHistory } from "react-router-dom";
import {useEffect} from 'react';
const history = useHistory();
useEffect(()=>{
// change if the user has successfully logged in and redirect to home
//screen
if(state.isLoggedIn){
history.push('/home');
}
// add this in order to listen to state/store changes in the UI
}, [state.isLoggedIn])
A simple solution would be to check for a state change in componentDidUpdate. Once your state has updated as a result of a successful redux action, you can compare the new props to the old and redirect if needed.
For example, you dispatch an action changing the value of itemOfInterest in your redux state. Your component receives the new state in its props, and you compare the new value to the old. If they are not equal, push the new route.
componentDidUpdate(prevProps: ComponentProps) {
if (this.props.itemOfInterest !== prevProps.itemOfInterest) {
this.props.history.push('/');
}
}
In your case of deleting an item, if the items are stored in an array, you can compare the new array to the old.

Resources