Redux observable cancel next operator execution? - redux

I am using redux-observable with redux for async actions. Inside epic's map operator i am doing some pre processing because its the central place.
My app calling same action from multiple container components with different values.
So basically i have to cancel my ajax request/next operator execution if deepEqual(oldAtts, newAtts) is true
code -
export default function getProducts(action$, store) {
return action$.ofType(FETCH_PRODUCTS_REQUEST)
.debounceTime(500)
.map(function(action) {
let oldAtts = store.getState().catalog.filterAtts
let newAtts = Object.assign({}, oldAtts, action.atts)
if (deepEqual(oldAtts, newAtts)) {
// Don't do new ajax request
}
const searchString = queryString.stringify(newAtts, {
arrayFormat: 'bracket'
})
// Push new state
pushState(newAtts)
// Return new `action` object with new key `searchString` to call API
return Object.assign({}, action, {
searchString
})
})
.mergeMap(action =>
ajax.get(`/products?${action.searchString}`)
.map(response => doFetchProductsFulfilled(response))
.catch(error => Observable.of({
type: FETCH_PRODUCTS_FAILURE,
payload: error.xhr.response,
error: true
}))
.takeUntil(action$.ofType(FETCH_PRODUCTS_CANCEL))
);
}
Not sure whether its right way to do it from epic.
Thanks in advance.

You can do this:
export default function getProducts(action$, store) {
return action$.ofType(FETCH_PRODUCTS_REQUEST)
.debounceTime(500)
.map(action => ({
oldAtts: store.getState().catalog.filterAtts,
newAtts: Object.assign({}, oldAtts, action.atts)
}))
.filter(({ oldAtts, newAtts }) => !deepEqual(oldAtts, newAtts))
.do(({ newAtts }) => pushState(newAtts))
.map(({ newAtts }) => queryString.stringify(newAtts, {
arrayFormat: 'bracket'
}))
.mergeMap(searchString => ...);
}
But most likely you do not need to save the atts to state to do the comparison:
export default function getProducts(action$, store) {
return action$.ofType(FETCH_PRODUCTS_REQUEST)
.debounceTime(500)
.map(action => action.atts)
.distinctUntilChanged(deepEqual)
.map(atts => queryString.stringify(atts, { arrayFormat: 'bracket' }))
.mergeMap(searchString => ...);
}

Related

vuexfire firestoreAction, binding with arg

I'm trying to bind my module's store to a document
import Vue from 'vue'
import { db } from '../my-firebase/db'
import { firestoreAction } from 'vuexfire'
export const user = {
...
actions: {
logOutUser: ({ commit }) => {
commit('logOutUser')
},
logInUser: ({ dispatch, commit }, userInfo) => {
let dbRef = db.collection('users').doc(userInfo.uid)
dbRef.update({ authInfo: userInfo })
.then(() => {
commit('logInUser', userInfo)
})
dispatch('bindFirebaseUser', dbRef)
},
bindFirebaseUser: (context, userRef) => {
console.log('Running dispatch BindFirebaseUser')
return firestoreAction(({ bindFirestoreRef }) => {
// return the promise returned by `bindFirestoreRef`
console.log('userRef:')
console.log(userRef)
return bindFirestoreRef('firebaseData', userRef)
})
}
}
}
It's not working. How do I bindFirestoreRef with the argument userRef? It doesn't seem to bind, though I can write to the firestore properly, so I would assume that my db is set up correctly.
It just doesn't give any form of error whatsoever, but if it binds, it should populate my store with the data I wrong shouldn't it?
You can pass the reference as the second argument to firestoreAction
bindFirebaseUser: firestoreAction(({ bindFirestoreRef }, userRef) => {
return bindFirestoreRef('firebaseData', userRef)
})

How to execute this prop as a function in the if/else statement?

I want to change the state in the redux reducer if GoogleMap DirectionService returns an error.
How to use redux-thunk logic in the redux actions file if I use react-google-maps package and the app receiving data inside the component file that uses this package?
componentDidMount() {
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route({
//some state
}, (result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: {...result},
markers: true
})
} else {
this.props.HOW_TO_EXECUTE_THIS_PROP?;
}
});
}
const mapDispatchToProps = (dispatch) => {
return {
HOW_TO_EXECUTE_THIS_PROP?: () => dispatch(actions.someAction()),
}
}
Generally, you will be able to simply call the prop method you're passing in. So if your code reads:
const mapDispatchToProps = (dispatch) => {
return {
propToExecute: () => dispatch(actions.someAction()),
}
}
... then you will call it inside your componentDidMount as:
this.props.propToExecute();
However, since we're using ES6, let's format it correctly, please:
const mapDispatchToProps = dispatch => ({
propToExecute: () => dispatch(actions.someAction())
})

Dispatch in middleware leads to action with wrong type

I'm trying to create a simple middleware to handle socket events.
const join = (channel) => (dispatch) => {
dispatch({
type: 'ACTION-1',
socketChannel: {...},
events: [...],
});
};
I dispatch this action that triggers it. And now when the dispatch method was called in my middleware with type 'ACTION-2' and received socketData as a payload, I see in my console what 'ACTION-1' was triggered twice and in the last time it is came with my socketData payload.
I wonder why 'ACTION-1' was registered instead 'ACTION-2' and how I can fix it? I would appreciate your help.
import { socket } from 'services/socket';
const socketMiddleware = ({ dispatch }) => next => (action) => {
const {
channel,
events, // an array of events for the channel
...rest
} = action;
if (typeof action === 'function' || !channel) {
return next(action);
}
const {
type,
name,
} = channel;
const channelInstance = socket.instance[type](name);
events.forEach((event) => {
const handleEvent = (socketData) => {
dispatch({ type: 'ACTION-2', socketData, ...rest });
};
channelInstance.listen(event.name, handleEvent);
});
return next(action);
};
export {
socketMiddleware
};
looks like you are not pathing the channel in your initial dispatch and you are failing your middleware finishes inside this if:
if (typeof action === 'function' || !channel) {
return next(action);
}
in order to fix this you should add channel in your dispatch:
const join = (channel) => (dispatch) => {
dispatch({
type: 'ACTION-1',
socketChannel: {...},
events: [...],
channel: { type: '...', name: '...' }
});
};

shouldn't I dispatch an action inside a .then statement?

I found a code on git which I'm trying to understand and in the code the guy have this function:
export function startAddTodo(text) {
return (dispatch, getState) => {
const UID = firebase.auth().currentUser.uid;
const todo = {
text,
isDone: false,
isStarred: false
};
const todoRef = firebaseRef.child(`todos/${UID}`).push(todo);
dispatch(addTodo({
id: todoRef.key,
...todo
}));
todoRef.then(snapshot => {
return;
}, error => {
Alert.alert(JSON.stringify(error.message));
});
};
}
Why shouldn't it be like
const todoRef = firebaseRef.child(`todos/${UID}`).push(todo);
todoRef.then(snapshot => {
dispatch(addTodo({
id: snapshot.key,
...todo
}));
})
I think this because the promise may be rejected, but in the first code he may get an error when trying to call todoRef.key inside the dispatch method.

multiple dispatch in redux action

I wanted to dispatch an action from another action but not able to do so. When I try to do so it not able to found getAllUser method.
Below is my action class.
export const myActions = {
getAllUser() {
return (dispatch) => {
makeApiCall()
.then((response) => {
dispatch({
type: USER_SUCCESS,
payload: response,
});
})
.catch((error) => {
dispatch({
type: USER_FAILURE,
payload: error,
});
});
};
},
addUser(user) {
return (dispatch) => {
makeApiCall(user)
.then((response) => {
/*
Need help here :
wants to call above getAllUser()
.then(() =>
dispatch({
type: ADD_SUCCESS,
payload: response,
});
)
*/
};
},
};
I have tried various approaches like,
myActions.getAllUser()
.then((response) =>
dispatch({
type: ADD_SUCCESS,
payload: response,
});
);
and trying do dispatch directly,
const self = this;
dispatch(self.getAllUser());
dispatch({
type: ADD_SUCCESS,
payload: response,
});
One more way around this is after addUser success, update the reducer and than from UI call getAccount again to refresh the results, but just curious to know on how can I achieve this using multiple dispatch.
You can export the functions individually instead of wrapping it under the same object:
export const getAllUser = () => dispatch => { ... }
export const addUser = () => dispatch => {
...
dispatch(getAllUser());
}
You can still import them all if desired:
import * as myActions from '...';
Or you can declare getAllUser first then add to myActions, but the above solution is much cleaner.
const getAllUser = ...
const myActions = {
getAllUser,
addUser = ... { dispatch(getAllUser()) }
}

Resources