Undefined in store reducer - javascript

Im trying to write reducer for action in angular ngrx store architecture:
this is my reducer:
export const registration = (state: any, {type, payload}) => {
switch (type) {
case 'REGISTER_USER':
console.log('reducer ' + payload, type);
return payload;
default:
return state;
}
};
and this is my function calling the reducer
register(user: RegistrationUser) {
return this.http.post(Global.API_URL+'/register', user)
.map(response => response.json())
.map(data => ({type: 'REGISTER_USER', data}))
.subscribe(action => {this.store.dispatch(action); console.log(action)});
}
the problem Im having in that the payload is undefined. Where the
console.log(action);
returns object. And console log from reducer returns proper action type but undefined as object 'reducer undefined REGISTER_USER'

I think you need to just map data to payload: data:
register(user: RegistrationUser) {
return this.http.post(Global.API_URL+'/register', user)
.map(response => response.json())
.map(data => ({type: 'REGISTER_USER', payload: data}))
.subscribe(action => {this.store.dispatch(action); console.log(action)});
}

Related

Promise.all() keeps running even after error is thrown

in my React component, I have this:
componentDidMount() {
const {
dispatchGetCompanies,
dispatchGetDocumentTopics,
dispatchGetDataForCompanyTopicList,
dispatchGetDocumentTypes,
dispatchRequestComplete,
dispatchRequestInProgress,
dispatchRequestError,
} = this.props;
dispatchRequestInProgress()
Promise.all([
dispatchGetCompanies(),
dispatchGetDocumentTopics(),
dispatchGetDocumentTypes(),
dispatchGetDataForCompanyTopicList(),
]).then(() => {
dispatchRequestComplete();
}).catch((error) => {
dispatchRequestError(error);
});
}
const mapDispatchToProps = function (dispatch) {
return {
dispatchGetCompanies: (...args) => dispatch(getCompanies(...args)),
dispatchGetDocumentTopics: (...args) => dispatch(getDocumentTopics(...args)),
dispatchGetDataForCompanyTopicList: (...args) => dispatch(getDataForCompanyTopicList(...args)),
dispatchDeleteCompanyTopic: (...args) => dispatch(deleteCompanyTopic(...args)),
dispatchGetDocumentTypes: (...args) => dispatch(getDocumentTypes(...args)),
dispatchRequestInProgress: (...args) => dispatch(requestInProgress(...args)),
dispatchRequestComplete: (...args) => dispatch(requestComplete(...args)),
dispatchRequestError: (...args) => dispatch(requestError(...args)),
};
};
The action for getDocumentTypes is this:
export function getDocumentTypes() {
return function (dispatch, getState, api) {
return api.get('/document-type-metadata')
.then(({data}) => dispatch(getDocumentTypesSuccess(data)));
};
}
export function getDocumentTypesSuccess(data) {
return {
type: GET_DOCUMENT_TYPES,
payload: data,
};
}
Reducer code is this:
function setDocumentTypes(state, action) {
const {
documentTypeMetadata
} = action.payload;
return state.merge({
documentTypes: List(documentTypeMetadata),
_metadata: Map({
isFetching: false,
}),
});
}
export default function (state = Map({
id: null,
companyTopics: List(),
companies: List(),
topics: List(),
documentTypes: List(),
_metadata: Map({
isFetching: false,
error: false,
}),
}), action) {
switch (action.type) {
case SECTION_IDENTIFICATION_REQUEST_COMPLETE:
return setRequestComplete(state, action);
case SECTION_IDENTIFICATION_REQUEST_IN_PROGRESS:
return setInProgress(state, action);
case SECTION_IDENTIFICATION_REQUEST_ERROR:
return setError(state, action);
case GET_COMPANIES:
return setCompanies(state, action);
case GET_DOCUMENT_TOPICS:
return setTopics(state, action);
case GET_COMPANY_TOPIC_LIST_DATA:
return setCompanyTopicListData(state, action);
case GET_DOCUMENT_TYPES:
return setDocumentTypes(state, action);
default:
return state;
}
}
The dispatchRequestError is similar; it sets the "error" object in the Redux store to whatever error is returned.
The problem I am having is that if the api call to getDocumentTypes returns back an error, I would expect the dispatchRequestError to immediately execute, with all the other promises (dispatchGetDocumentTopics, etc) being rejected. However, after the error is set, the other promises inside the Promise.all() still resolve, causing the error property to become undefined, which causes the error message that I'm showing on the page to appear, then quickly disappear.
What is going wrong?
Thank you

How to pass return value from component to another one

I have a const that assembles a get (in another component) and returns me a verification code, this is: response.data.verifyCode
This component is called through a submit on another component.
I need to get this value in my another const, which is below:
export const sendCode = (id, username) => (dispatch) => {
dispatch({ some code here });
return registerAccount
.sendCode(id, username)
.then((response) => {
dispatch({ payload: response.data.verifyCode });
return response.data;
})
.catch(() => {
return null;
});
};
export const getCodeAndVerify = (id, userCode) => (dispatch) => {
dispatch({ some code here });
const getVerifyCode = // I need to get response.data.verifyCode from sendCode above
// I try to use
// const getVerifyCode = { verifyCode: sendCode() };
// but this returns [object object]
return registerAccount
.getCodeAndVerify(id, userCode, getVerifyCode)
.then(() => {
// some code here
})
.catch(() => {
// some code here
});
};
That is, I need to get the verifyCode from the return from the superior const and use it in the other const, but I'm not sure how to do that. Can someone help me?
Asynchronous actions (I'm assuming thunks) also receive a getState second argument after dispatch. Assuming there's a reducer to handle the verifyCode send code success, you can access the store and retrieve the verifyCode value in getCodeAndVerify.
export const sendCode = (id, username) => (dispatch) => {
dispatch({ some code here });
return registerAccount
.sendCode(id, username)
.then((response) => {
dispatch({
type: 'VERIFY_CODE_SUCCESS', // <-- action object needs type
payload: response.data.verifyCode,
});
return response.data;
})
.catch(() => {
return null;
});
};
export const getCodeAndVerify = (id, userCode) => async (dispatch, getState) => {
dispatch({ type: TYPES.PASS_CREATION_REQUESTED });
const getVerifyCode = getState().path.to.verifycode; // <-- retrieve from state
return registerAccount
.getCodeAndVerify(id, userCode, getVerifyCode)
.then(() => {
// some code here
})
.catch(() => {
// some code here
});
};

Redux-thunk: API response keeps pending

I am trying to develop an application, that is showing photos from Unsplash given a keyword. I managed to fetch specific photos using unsplash.js. In my actions, I have several action creators:
export const fetchPhotos = () => ({
type: FETCH_PHOTOS
});
export const receivePhotos = term => {
const unsplash = new Unsplash({
applicationId:
"id",
secret: "secret",
callbackUrl: "callback"
});
console.log(term);
const response = unsplash.search
.photos(term, 1, 20)
.then(toJson)
.then(json => json)
.then(json => json)
console.log(response.then(results => results));
return {
type: RECEIVE_PHOTOS,
payload: response
};
}
export const unsplash = (term) => dispatch => {
console.log(term);
dispatch(fetchPhotos());
setTimeout(() => {
dispatch(receivePhotos(term));
console.log("dispatching")
return Promise.resolve();
}, 1000)
}
My reducers then do:
const initialState = {
isFetching: false,
sortDirection: null,
sortKey: null,
items: []
}
export default function(state = initialState, action) {
switch (action.type) {
case FETCH_PHOTOS:
console.log(state, "Fetch photos reducer");
return {
...state,
isFetching: true
};
case RECEIVE_PHOTOS:
console.log("Receive photos reducer", action.payload)
return {
...state,
isFetching: false,
items: action.payload
};
case SET_SORT:
return {
...state,
sortKey: action.sortKey,
sortDirection: action.sortDirection
};
default:
return state;
}
}
However, as the receivePhotos action creator calls an API, I have a promise that needs to be resolved in order for the whole application to work. My fetch photos reducer is console logging the action, then the Promise appears, however it is always on pending. Then my receivePhotos action creator dispatches to the reducer and I can see that this is a Promise:
How can I resolve this promise?
In the below code you assign a promise to response, then console.log that promise, and then return the action with payload set to that promise.
const response = unsplash.search
.photos(term, 1, 20)
.then(toJson)
.then(json => json)
.then(json => json)
console.log(response.then(results => results));
return {
type: RECEIVE_PHOTOS,
payload: response
};
dispatch(receivePhotos(term)); then dispatches that action, still with the payload as a promise. maybe this would work if you had middleware that could handle it.
This use of dispatch suggests that you are using redux-thunk though.
In that case, you should do the same with receivePhotos, include the fetchPhotos call, and retire the unsplash action.
const unsplashClient = new Unsplash({
applicationId:
"id",
secret: "secret",
callbackUrl: "callback"
});
export const receivePhotos = term => dispatch => {
dispatch(fetchPhotos());
return unsplashClient.search
.photos(term, 1, 20)
.then(toJson)
.then(json => dispatch({
type: RECEIVE_PHOTOS,
payload: json
});
}
In the end I would suggest a bit of refactoring of the actions and the (related reducers) such as:
const unsplashClient = new Unsplash({
applicationId:
"id",
secret: "secret",
callbackUrl: "callback"
});
export const fetchingPhotos = payload => ({
type: FETCHING_PHOTOS, payload
});
export const setPhotos = payload => ({
type: SET_PHOTOS, payload
});
export const fetchPhotos = term => dispatch => {
dispatch(fetchingPhotos(true));
return unsplashClient.search
.photos(term, 1, 20)
.then(toJson)
.then(json => {
dispatch(setPhotos(json));
dispatch(fetchingPhotos(false));
});
}

Proper way to call two async action

How to call an async action after another action was successfully dispatched?
I am learning Redux and I have some questions, about async action.(I am using thunk)
I have two action:
export const addToCart = addToCartData => dispatch => {
axios.post("/api/cart/add-to-cart", {addToCartData)
.then(res => {
dispatch({ type: ADD_TO_CART, payload: res.data });
})
.catch(err => console.log(err));
};
export const removeProduct = (userID) => dispatch => {
axios
.delete(`/api/wait-list/remove/${userID}`)
.then(res => {
dispatch({ type: REMOVE_FROM_WAITLIST, payload: res.data });
})
.catch(err => console.log(err));
};
And I want to execute removeProduct action only after addToCart will be successfully executed! I am trying to do third one with two of them, it looks like this:
export const addToCartAndPemoveProduct = (data) => dispatch => {
dispatch(addToCart(data)
dispatch(removeProduct(data));
But it executes removeProduct action first, and after addToCart....
How do I can do it right due to order? Maybe I should return a promise from first one and execute second one after it will be successfull resolve? It will be looking like this:
export const addToCart = addToCartData => dispatch => {
return axios.post("/some", {addToCartData)
.then(res => { dispatch({ type: ADD.....})
};
export const addToCartAndPemoveProduct = (data) => dispatch => {
dispatch(addToCart({ userID, productId }))
.then(data => {
dispatch(removeProduct({ userID, productName, productDescr }));
})
}
Is it ok or not?
Do you always want to dispatch removeProduct after addToCart? In this case:
export const addToCart = addToCartData => dispatch => {
// You'll have to get the userId here first, probably from getState() if it's not being passed in.
axios.post("/api/cart/add-to-cart", {addToCartData)
.then(res => {
dispatch({ type: ADD_TO_CART, payload: res.data });
dispatch(removeProduct(userId));
})
.catch(err => console.log(err));
};
Maybe rename the action to moveProductFromWaitListToCart to express the full transaction.

Using a callback while dispatching actions

I am trying to make a GET request for some data.
Here is my action call.
componentDidMount() {
this.props.fetchData(() => {
this.setState({ isLoading: false });
});
}
Prior to completion I'd like to display "Loading..." momentarily as the fetch request is making it's trip. I'm using a callback for this and setting my local state.
Here is my action creator with a 'callback'.
export function fetchData(callback) {
return (dispatch) => {
axios.get(`/api/fetchsomething`)
.then(() => callback())
.catch((err) => {
console.log(err.message);
});
}
}
And here is that same function above but dispatching the action so that I can receive as props and render to my ui.
export function fetchData(callback) {
return (dispatch) => {
axios.get(`/api/fetchsomething`)
.then((response) => dispatch({ type: FETCH_DATA, payload: response }))
.catch((err) => {
console.log(err.message);
});
}
}
My question is how do you make the callback and dispatch the action in the same action creator function? Is that even good practice?
You could do something like this
componentDidMount() {
this.setState({ isLoading: true }, () => {
// ensuring that you make the API request only
// after the local state `isLoading` is set to `true`
this.props.fetchData().then(() => this.setState({ isLoading: false });
});
}
and, fetchData would be defined as follows
export function fetchData(callback) {
return (dispatch) => {
return axios.get(`/api/fetchsomething`)
.then((response) => dispatch({ type: FETCH_DATA, payload: response }))
.catch((err) => console.log(err.message));
}
}
If you're using the redux-thunk middleware to use asynchronous actions, then these actions will return Promises; so you can set your component's local state after that Promise resolves.
In the component:
.....
componentDidMount() {
this.props.fetchData();
}
....
export default connect((state) => ({loading: state.loading, data: state.data}))(Component);
In the actions, you should do :
....
export function fetchData() {
return (dispatch) => {
dispatch({ type: FETCHING_DATA}); //dispatch an action for loading state to set it to true
return axios.get(`/api/fetchsomething`)
.then((response) => dispatch({ type: DATA_FETCHED, payload: response }))
.catch((err) => console.log(err.message));
}
}
....
In the reducer, you should do :
....
case 'FETCHING_DATA':
return {
...state,
loading: true,
}
case 'DATA_FETCHED':
return {
...state,
data: action.payload,
loading: false,
}
....
I personally feel that you shouldn't put any business logic in your component because it can cause some problems later when you want to refactor your app. This means that there shouldn't be any .then in your component and everything should be guided through redux (if there is some side effects in your app). So, you should control your loading state from redux itself and not inside the component.

Categories