Redux store get undefined in action.data, even though my reducer function gets the correct result from the api, my dispatch is in the correct place.
My Reducer function:
const convertPosition = StateHelper.createAsyncOperation(MODULE, 'convertPosition');
export function $convertPosition(lat, long) {
return (dispatch) => {
dispatch(Activity.$processing(MODULE, $convertPosition.name));
dispatch(convertPosition.request());
return fetch(`${API_ENDPOINT}/shared/maps/address?latlong=${lat},${long}`, {
headers: {
Authorization: `Bearer ${AuthService.getAccessToken()}`,
},
})
.then(FetchHelper.ResponseHandler, FetchHelper.ErrorHandler)
.then((result) => dispatch(convertPosition.success(result)))
.catch((error) => dispatch(convertPosition.failure(error)))
.finally(() => dispatch(Activity.$done(MODULE, $convertPosition.name)));
};
}
My Reducer statement:
case convertPosition.SUCCESS:
return {
...state,
locationName: action.data,
};
case convertPosition.FAILURE:
return {
...state,
locationName: null,
};
After I checked the API with postman, the result was an array so the code was:
...
.then((result) => dispatch(convertPosition.success({result: result})))
....
Related
I want to attach params to react redux fetch action and I searched for many days the redux docs, but even after trying out a few things i am getting this error:
[Unhandled promise rejection: Error: Actions must be plain objects. Use custom middleware for async actions.]
https://codesandbox.io/s/fast-framework-ct2fc?fontsize=14&hidenavigation=1&theme=dark
The original action looks like this:
export function fetchArticleDetails() {
return apiAction({
url: "http://myurl/appApi/2.0.0/getData/1", //1 should be an optional value
onSuccess: setArticleDetails,
onFailure: () => console.log("Error occured loading articles"),
label: FETCH_ARTICLE_DETAILS
});
}
function setArticleDetails(data) {
console.log(data);
return dispatch({
type: SET_ARTICLE_DETAILS,
payload: data
});
}
i tried to set the param directly
export function fetchArticleDetails(id)
...
url: `http://myurl/appApi/2.0.0/getData/${id}`,
or some variations to put the params in the payload directly
function setArticleDetails(data) {
console.log(data);
return dispatch({
type: SET_ARTICLE_DETAILS,
payload: data,
userid: id
});
}
All this results in the same error. Anyone have an idea where to place the dynamic data to solve it?
Another idea could be to set the params in my reducer maybe?
Update store/index.js
import { createStore, applyMiddleware } from "redux";
import rootReducer from "../reducers";
import apiMiddleware from "../middleware/api";
const store = createStore(rootReducer, applyMiddleware(apiMiddleware));
window.store = store;
export default store;
update: middleware/api.js
import axios from "axios";
import { API } from "../actions/types";
import { accessDenied, apiError, apiStart, apiEnd } from "../actions/api";
const apiMiddleware = ({ dispatch }) => next => action => {
next(action);
if (action.type !== API) return;
const {
url,
method,
data,
accessToken,
onSuccess,
onFailure,
label,
headers
} = action.payload;
const dataOrParams = ["GET", "DELETE"].includes(method) ? "params" : "data";
// axios default configs
axios.defaults.baseURL = process.env.REACT_APP_BASE_URL || "";
axios.defaults.headers.common["Content-Type"] = "application/json";
axios.defaults.headers.common["Authorization"] = `Bearer ${accessToken}`;
if (label) {
dispatch(apiStart(label));
}
axios
.request({
url,
method,
headers,
[dataOrParams]: data
})
.then(({ data }) => {
dispatch(onSuccess(data));
})
.catch(error => {
dispatch(apiError(error));
dispatch(onFailure(error));
if (error.response && error.response.status === 403) {
dispatch(accessDenied(window.location.pathname));
}
})
.finally(() => {
if (label) {
dispatch(apiEnd(label));
}
});
};
export default apiMiddleware;
function apiAction()
function apiAction({
url = "",
method = "GET",
data = null,
accessToken = null,
onSuccess = () => {},
onFailure = () => {},
label = "",
headersOverride = null
}) {
return {
type: API,
payload: {
url,
method,
data,
accessToken,
onSuccess,
onFailure,
label,
headersOverride
}
};
}
There are a couple of issues with the code. apiMiddleware should only pass the action to the next middleware in the chain if it's not of type API.
const apiMiddleware = ({ dispatch }) => (next) => (action) => {
if (action.type !== API) {
return next(action)
}
// do stuff
}
Since the apiMiddleware dispatches what onFailure returns, the function has to return an object. In fetchArticleDetails, you're passing () => console.log("Error occured loading articles") causing apiMiddleware to dispatch undefined.
export function fetchArticleDetails(id) {
return apiAction({
url: `https://jsonplaceholder.typicode.com/todos/${id}`,
onSuccess: setArticleDetails,
onFailure: (error) => ({
type: FETCH_ARTICLE_ERROR,
payload: error
}),
label: FETCH_ARTICLE_DETAILS
})
}
CodeSandbox
I would strongly recommend using React Query to simplify data fetching, managing, and syncing server state.
I'm making an API Call to get a value that i will store in my reducer. Although when i'm calling my reducer i'm getting null.
here is my code to get the value :
export function getValue(token) {
return fetch('URI', { method: 'GET'})
.then(response => response.json())
.then((response) => {
return response.value;
});
}
Then i have my redux :
const UPDATE_VALUE = 'user/UPDATE_VALUE';
...
const initialState = {
myValue: null,
};
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case UPDATE_VALUE:
return {
...state,
myValue: action.value
};
default:
return state;
}
}
export function updateValue(value) {
return {
type: UPDATE_VALUE,
value,
};
}
And here i call the api to stock the value. I'm getting the value and it existes but when i want to store and get it's value i'm getting null :
const getMyValue = new Promise((resolve) => {
return getMyValue(token).then((value) => {
updateValue(value === null ? null : value);
resolve();
});
});
I'm working on a RN app, which has redux in it. Now I can login with the help of jwt but when Im trying the to get the data from my other component its giving me 403 error. Please find below the relevant code.
Here is my reducer:
const initState = {
isLoadingCollegeDashList : false,
collegeDashList:{},
collegeDashListFail:false
}
const collegeReducer = ( state = initState, action) => {
switch(action.type){
case 'IS_LOADING_COLLEGE_DASH_LIST' :
return{
...state,
isLoadingCollegeDashList: true,
collegeDashList : false
}
case 'COLLEGE_DASH_LIST' :
return {
...state,
isLoadingCollegeDashList : false,
collegeDashList : true,
userData : action.userData
}
case 'COLLEGE_DASH_LIST_FAIL' :
return{
...state,
isLoadingCollegeDashList:false,
collegeDashList: false,
collegeDashListFail: action.error
}
default :
return state
}
}
and here's my action that's making get request
export const populateCollege = (token) => {
const headers = {
'api-secret' : ...secret...,
'authorization':...authToken...,
'Content-Type': 'application/json',
}
return dispatch => {
dispatch(isLoadingCollegeDashList(true));
return axios.get( '...api/api/...', {
},{
headers:headers,
})
.then((response) => {
if(response.status < 300){
dispatch(isLoadingCollegeDashList(false))
dispatch(collegeDashList(response))
console.log(response);
}
else{
response.json().then((responseJSON) => {
console.log("responseJSON",responseJSON);
dispatch(isLoadingCollegeDashList(false))
dispatch(collegeDashListFail(responseJSON.message))
})
}
})
.catch((error) => {
console.log("error",error);
dispatch(isLoadingCollegeDashList(false))
dispatch(collegeDashListFail(error))
})
}
}
export const isLoadingCollegeDashList = (bool) => {
return{
type:'IS_LOADING_COLLEGE_DASH_LIST',
isLoadingCollegeDashList:bool
}
}
export const collegeDashList = (userData) => {
return{
type:'COLLEGE_DASH_LIST',
userData
}
}
export const collegeDashListFail = (error) => {
return{
type:'COLLEGE_DASH_LIST_FAIL',
error
}
}
here's action that im calling if you want to check it
const mapDispatchToProps = dispatch => ({
populateCollege : (token) => dispatch(actions.populateCollege({token}))
});
PS I've for now stored token in the state of one hence passing the token from this dispatch itself.
Let me know if you need any clarification / more information then do let me know. Thanks in advance
Make sure you have the authorisation schema before your token. The schema can be like Basic, Bearer or any other value based on your authorisation details. (eg. Authorization: Bearer TOKEN).
Also, try to reuse your auth headers while creating the axios instance so you won't need to inject them on every call.
I have a function that calls a method that is in my Helper.js file.
import { getTest } from '../../common/Helper';
...
myMethod() {
...
const test = getTest(this.state.myID);
console.log(test);
}
...
My Helper.js:
export const getTest = (pID) => {
axios.get('http://myserver.com/', {
params: {
method: 'getVacantUnits',
propertyID: pID
}
}).then((response) => {
console.log(response.data);
return response.data;
}).catch((error) => {
// handle error
console.log(error);
return 0;
});
};
It is odd because my output is:
undefined
myDataContent
It looks like that "const test" is receiving undefined before the getTest being run. Why is it happening?
Thanks
It's returning this first since it's not awaiting the result:
console.log(test);
2 easy ways to fix this I am showing below, first with promise:
const test = getTest(this.state.myID).then(response=> console.log(response)).catch(err => console.log(err))
Add in return as well since you need to return from outermost function
export const getTest = (pID) => {
return axios.get('http://myserver.com/', {
params: {
method: 'getVacantUnits',
propertyID: pID
}
}).then((response) => {
console.log(response.data);
return response.data;
}).catch((error) => {
// handle error
console.log(error);
return 0;
});
};
second using async await:
// add in await
export const getTest = async (pID) => {
return axios.get('http://myserver.com/', {
params: {
method: 'getVacantUnits',
propertyID: pID
}
}).then((response) => {
console.log(response.data);
return response.data;
}).catch((error) => {
// handle error
console.log(error);
return 0;
});
};
// here you are awaiting the response before you run console.log
const test = await getTest(this.state.myID);
console.log(test);
You can solve this in several other ways, but I think these are the 2 easiest. Basically think about the fact that those are run synchronously and the console.log executes before the function returns, so if you "wait" then it makes it so the console.log() is dependent on the first function executing first.
I have an async function that uses redux and it calls an API and returns the response from the server:
function xyz() {
return async (dispatch, getState) => {
const { user: { token } } = getState();
return axios.get(API_URL, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
'jwt': token
}
})
.then(response => {
console.log(response.data.data);
return response.data.data;
})
.catch(function(error) {
console.log('error: ' + error.message);
});
};
}
The mapDispatchToProps function is defined as it follows:
const mapDispatchToProps = (dispatch, ownProps) => {
return {
xyz: () => {
dispatch(actions.xyz());
}
};
};
export default connect(null, mapDispatchToProps)(Container);
I'm trying to call the function xyz from the following function:
abc = async () => {
const { xyz } = this.props;
const result = await xyz();
console.log(result);
}
which is triggered when a button is pressed:
<TouchableOpacity onPressOut={this.abc}>
I see that the console.log into the function abc prints undefined, while the console.log(result.data.data) into xyz prints the expected result. Where am I wrong?
Solution
The error was in the mapDispatchToProps function, which was missing the return. Here it is the correct implementation:
const mapDispatchToProps = (dispatch, ownProps) => {
return {
xyz: () => {
return dispatch(actions.xyz());
}
};
};
export default connect(null, mapDispatchToProps)(Container);
Like I mentioned in the comments, xyz() should return a promise. Double check that the dispatch and getState arguments are getting passed correctly. You'll also need to await the returned axios.get promise. For example:
abc = async () => {
const { xyz } = this.props;
const result = await xyz();
const results = await result();
console.log(results);
}
Here is a snack with a working example.
I don't fully understand what you mean when you say the console.log in xyz prints the expected result, are you speaking about the error.message in your catch? Also, the reason you may be receiving undefined is because function xyz is not Async but rather it returns an async function, thus you should make xyz async such as:
function async xyz () {
// Rest of code
}
For more you can read here