React/Redux - Dispatch method - returning error - javascript

I'm currently trying to add and delete my items on the page and it's returning an error.
Unhandled rejection (TypeError): Cannot read property data of undefined pointing to .catch in both of in the below code.
export const addItem = (item) => (dispatch,
getState) => {
axios
.post('/api/items', item, tokenConfig(getState))
.then(res => dispatch({
type: ADD_ITEM,
payload: res.data
}))
.catch(err => dispatch(returnErrors(err.response.data, err.response.status))
);
};
export const deleteItem = (id) => (dispatch, getState) => {
axios
.delete(`/api/items/${id}`, tokenConfig(getState))
.then(res => dispatch({
type: DELETE_ITEM,
payload: id
}))
.catch(err => dispatch(returnErrors(err.response.data, err.response.status))
);
};
/////////////////////////////////////////////////
The returnErrors method referenced above is from another file that is here:
import { GET_ERRORS, CLEAR_ERRORS } from './types';
// RETURN ERRORS
export const returnErrors = (msg, status, id = null) => {
return {
type: GET_ERRORS,
payload: { msg, status, id }
};
};
// CLEAR ERRORS
export const clearErrors = () => {
return {
type: CLEAR_ERRORS
};
};
I have put a console.log(err.response) and a console.log(err.response.data) right above the dispatch(returnErrors(err.response.data, err.response.data)); and returned undefined for the first and uncaught (in promise) cannot read property of undefined
I was told by someone that
This essentially means your error object doesn't have correct data. Please look into the error object returned. It could be an issue with items/user api, it should return correct error object.
items api route
router.post('/', auth, (req, res) => {
const newItem = new Item({
name: req.body.name
})
newItem.save().then(item => res.json(item));
});
// DELETE api/items/:id
// Delete an item
// Private
router.delete('/:id', auth, (req, res) => {
Item.findById(req.params.id)
.then(item => item.remove().then(() => res.json({ deleted: true
})))
.catch(err => res.status(404).json({ deleted: false }));
})
Not sure where data is undefined. Anyone see anything missing?
You can take a look at what the chrome dev tools network tab returned here:
https://imgur.com/D5OGLpf
authActions
// Check token & Load User
// Want to check routes/auth.js for user by id that's included with token
// Going to use asynchronous request, use dispatch
export const loadUser = () => (dispatch, getState) => {
// User loading
dispatch({ type: USER_LOADING });
// Fetch user
axios.get('/api/auth/user', tokenConfig(getState))
.then(res => dispatch({
type: USER_LOADED,
payload: res.data
}))
.catch(err => {
dispatch(returnErrors(err.response.data, err.response.status));
dispatch({
type: AUTH_ERROR
});
});
};
// Register User
export const register = ({ name, email, password }) => dispatch => {
// Headers
const config = {
headers: {
'Content-Type': 'application/json'
}
}
// Request body
const body = JSON.stringify({ name, email, password });
axios.post('/api/users', body, config)
.then(res => dispatch({
type: REGISTER_SUCCESS,
payload: res.data
}))
.catch(err => {
dispatch(returnErrors(err.response.data, err.response.status, 'REGISTER_FAIL'));
dispatch({
type: REGISTER_FAIL
});
});
};
// LogIn
export const login = ({ email, password }) => dispatch => {
// Headers
const config = {
headers: {
'Content-Type': 'application/json'
}
}
// Request body
const body = JSON.stringify({ email, password });
axios.post('/api/auth', body, config)
.then(res => dispatch({
type: LOGIN_SUCCESS,
payload: res.data
}))
.catch(err => {
dispatch(returnErrors(err.response.data, err.response.status,
'LOGIN_FAIL'));
dispatch({
type: LOGIN_FAIL
});
});
};
// LogOut
export const logout = () => {
return {
type: LOGOUT_SUCCESS
};
};
// Setup config/headers and Token
export const tokenConfig = (getState) => {
// Get token from localstorage
const token = getState().auth.token;
// Headers
const config = {
headers: {
"Content-type": "application/json"
}
}
// Check if token exists, add to Headers
if(token) {
config.headers['x-auth=token'] = token;
}
return config;
}

Base your image https://imgur.com/D5OGLpf, your request to axios.delete('/api/items/${id} do not reach the route /api/items/:id.
Why I said so?
The response status is 401 (https://imgur.com/D5OGLpf), meaning that Unauthorized. The endpoint of the route router.delete('/:id' might be protected by the authentication middleware or something like that.
To solve it,
First
You need to make an authenticated request using the way you set up for your api either basic authentication, oauth[2], or your customized one.
Then
Before dispatch dispatch(returnErrors..., you need to check if the data exists.
axios
.delete(`/api/items/${id}`, tokenConfig(getState))
.then(res => dispatch({
type: DELETE_ITEM,
payload: id
}))
.catch(err => {
if(error.status === 404) {
// here, you are sure that error.response.data exists
dispatch(returnErrors(err.response.data, err.response.status)
}
else {
// do something else to handle the error
}
})
**Remember that ** the caught error can be anything ranging from your error status 400, 500,... to you un-caught error within the .then(...).

The inner promise to remove the item remains in a pending state and as you have noted doesn't return any response.
To have an idea what is happening.
Item.findById(req.params.id)
.then(item => item.remove().then(() => res.json({ deleted: true
})))
.catch(err => res.status(404).json({ deleted: false }));
})
can be simplified to
P.then(p => Q)
where P and Q are promises objects.
When P is fulfilled, it returns Q and Q continues to remain in a pending state waiting for it to be resolved.
You can resolve Q by flattening the then chain to handle when the remove operation is fulfilled.
Item.findById(req.params.id)
.then(item => item.remove())
.then(() => res.json({ deleted: true }))
.catch(err => res.status(404).json({ deleted: false }));

Related

Nodejs MongoDB - add new entry Post()

so trying to post new entry into MongoDB. but getting this error in Redux Product validation failed: name: Path 'name' is required., description: Path 'description' is required.
nodejs version: 14.9.0
and Atlas mongodb.
frontEnd addProduct.js page:
import { createProduct } from '../redux/actions/productActions'
const [name, setName] = useState('')
const [description, setDescription] = useState('')
const createProductHandler = (e) => {
e.preventDefault()
dispatch(createProduct({
name,
description
}))
}
const nameHandler = (e) => {
setName(e.target.value)
}
const descriptionHandler = (e) => {
setDescription(e.target.value)
}
return (
<input type='text' onChange={nameHandler} />
<input type='text' onChange={descriptionHandler} />
<input type="submit" value='submit' onClick={createProductHandler} />
)
productController:
const createdProduct = asyncHandler(async (req, res) => {
const mongoProduct = async (data) => {
return new Product({
name: data.name,
description: data.description
})
}
const product = await mongoProduct(req.body)
const createdProduct = await product.save()
res.status(201).json(createdProduct)
})
productActions:
export const createProduct = () => async (dispatch, getState) => {
try {
dispatch({
type: 'PRODUCT_CREATE_REQUEST',
})
const {
userLogin: {userInfo},
} = getState()
const config = {
headers: {
Authorization: `Bearer ${userInfo.token}`,
},
}
const { data } = await axios.post(`/api/products`, {}, config)
dispatch({
type: 'PRODUCT_CREATE_SUCCESS',
payload: data
})
} catch (error) {
dispatch({
type: 'PRODUCT_CREATE_FAIL',
payload:
error.response && error.response.data.message
? error.response.data.message
: error.meessage,
})
}
}
productReducers.js:
export const productCreateReducer = (state = {}, action) => {
switch (action.type) {
case 'PRODUCT_CREATE_REQUEST':
return {loading: true}
case 'PRODUCT_CREATE_SUCCESS':
return {loading: false, success: true, product: action.payload}
case 'PRODUCT_CREATE_FAIL':
return {loading: false, error: action.payload}
default:
return state
}
}
alternatively when i try to populate the database from post-man using this code in productController.js it works fine with sample data:
const createdProduct = asyncHandler(async (req, res) => {
const product = new Product({
name: 'Sample Name',
description: 'Sample Description'
})
const createdProduct = await product.save()
res.status(201).json(createdProduct)
})
plus im getting POST ipadress/api/products 500 (Internal Server Error) in console
You can config your axios api service config to separate file and use axios
const request = axios.create({
// baseURL: 'https://mock-api.com',
baseURL: BASE_URL ,
timeout: 5000
})
request.interceptors.request.use(
config => {
// get token
if (// check your token) {
config.headers["Authorization"] = "Bearer ${your-token}"
}
return config
},
error => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
}
)
// Can also config axios response interceptors to handle API error
Your redux action
import axiosInstance from './your-axios-config-path'
export const createProduct = (product) => async (dispatch, _getState) => {
try {
dispatch({ type: 'PRODUCT_CREATE_REQUEST' })
const response = await axiosInstance.post(`/api/products`, {...product})
dispatch({
type: 'PRODUCT_CREATE_SUCCESS',
payload: response?.data ?? {}
})
} catch (error) {
dispatch({
type: 'PRODUCT_CREATE_FAIL',
payload: // error message,
})
}
}
Alternatively, you can use Redux Toolkit, It much easier to setup store and using. It includes createAsyncThunk, RTK Query to handle side effect.

Redux: Unhandled Rejection (TypeError): Cannot read property 'data' of undefined

In my redux action file below I am getting the following error - Unhandled Rejection (TypeError): Cannot read property 'data' of undefined. The error is pointing to the line where the code is: dispatch(returnErrors(err.response.data, err.response.status, 'LOGIN_FAIL'));
//Action File Login User
export const login = ({email, password, history}) => dispatch => {
const config = {
headers: {
'Content-Type': 'application/json',
},
};
const body = JSON.stringify({email, password});
axios
.post('/api/user/login/', body, config)
.then(res => {
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
history.push('/userfeed');
})
.catch(err => {
dispatch(returnErrors(err.response.data, err.response.status, 'LOGIN_FAIL'));
dispatch({
type: LOGIN_FAIL,
});
});
};
//Return errors
export const returnErrors = (message, status, id = null) =>{
return{
type: GET_ERRORS,
payload: {message, status, id}
};
};

Uncaught (in promise) TypeError: Cannot read property 'data' of undefined React/Redux/Axios

When calling the action in question (postRequest), it returns this data undefined error, the action however is successful & refreshing clears the error.
error:
Uncaught (in promise) TypeError: Cannot read property 'data' of undefined:
payload: err.response.data
Front-end:
handleSubmit = event => {
event.preventDefault();
this.props.postRequest({ body: this.state.body });
};
export const postRequest = newRequest => dispatch => {
dispatch({ type: LOADING_UI });
axios
.post('/request', newRequest)
.then(res => {
dispatch({
type: POST_REQUEST,
payload: res.data
});
dispatch(clearErrors());
})
.catch(err => {
dispatch({
type: SET_ERRORS,
payload: err.response.data
});
});
};
Back-end:
exports.newRequest = (req, res) => {
if (req.body.body.trim() === "") {
return res.status(400).json({ body: "Body must not be empty" });
}
db.collection("requests")
.where("userHandle", "==", req.user.handle)
.where("status", "==", "awaiting")
.limit(1)
.get()
.then(data => {
if (!data.empty) {
return res
.status(403)
.json({ general: `Only 1 outstanding request at a time` });
}
return db
.collection("requests")
.add(newRequest)
.then(doc => {
const resRequest = newRequest;
resRequest.requestId = doc.id;
res.json({ resRequest });
});
})
.catch(err => {
res.status(500).json({ error: `sum ting wong` });
console.error(err);
});
};
I can't figure out why - if there is no error being caught - why the err.response.data payload being undefined would be a problem.
Any help will be greatly appreciated!
Update: Updated front-end w/ if statement per Axios docs, however now no error is thrown it just continues to load, still actions goes through & refreshing fixes.
.catch(err => {
if (err.response) {
dispatch({
type: SET_ERRORS,
payload: err.response.data
});
}
});
It’s throwing an error because you need to JSON.parse() the response before you access the data property.
axios
.post('/request', newRequest)
// add this
.then(resp => JSON.parse(resp))
.then(res => {
dispatch({
type: POST_REQUEST,
payload: res.data
});
dispatch(clearErrors());
})
.catch(err => {
dispatch({
type: SET_ERRORS,
payload: err.response.data
});
});
Without parsing the JSON your code can't access the data property so it throws an error.
And once it throws there is no response property on the error object.
Remove that line, add a console.log, and you will see it throw the error.

Axios.All (chaining multiple requests together) not working?

EDIT: I resolved it. The issue was that I was passing the userData object to something else that didn't require all of it. When I narrowed it down I got what I was hoping to achieve.
So I'm trying to make it so that when a user registers on my web app, it'll automatically log them in and then redirect to the home page. The redirect to home page is working, but it's not registering the user or setting the user's details. I've looked at the documentation and believe I'm doing it correctly, but still no results?
authActions.js File:
// Register User
export const registerUser = (userData, history) => dispatch => {
axios.post('/users/register', userData)
//.then(res => history.push('/login'))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
}
// Login - Get User Token
export const loginUser = (userData) => dispatch => {
axios.post('/users/login', userData)
.then(res => {
// Save to localStorage
const { token } = res.data;
// Set token to LocalStorage
localStorage.setItem('jwtToken', token);
// Set token to Auth header
setAuthToken(token);
// Decode token to get user data
const decoded = jwt_decode(token);
// Set current user
dispatch(setCurrentUser(decoded));
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
}
// Set logged in user
export const setCurrentUser = (decoded) => {
return {
type: SET_CURRENT_USER,
payload: decoded
}
}
// Register, Login, Set User all at the same time
export const regLogSetUser = (userData, history) => dispatch => {
axios.all([registerUser(), loginUser(), setCurrentUser()])
.then(res => history.push('/'))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
}
In my Register component, I'm also properly calling in the Prop Function (at the bottom) (after importing it at the top).
Register.propTypes = {
regLogSetUser: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
}
const mapStateToProps = (state) => ({
auth: state.auth,
errors: state.errors
});
export default connect(mapStateToProps, { regLogSetUser })(withRouter(Register));
Here is the link to the documentation:
https://github.com/axios/axios
And here is what it says for Axios.all()
Performing multiple concurrent requests
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
Thanks for the help. Still learning!

axios get request return request failed with error 400

I need help on solving this issue. I am new with react native and javascript. Now I am trying to hook up the react native application with API. This process require me to get the token first by axios.post before I can do axios.get to fetch the data.
Long story short, below is my code snippet for both.
... // code
const TOKEN_URL = 'https://test.co/testing/tokens'
const DATA_URL = 'https://test.co/testing/data/page1'
const getToken = () => {
axios.post(TOKEN_URL, {
email: 'email',
password: 'password',
role: 'user'
})
.then((response) => {
//console.log(response.data.token);
return response.data.token;
})
.catch((error) => {
console.log(error);
});
};
//'export' here is for use in other code: example onPress function
export const fetchDriver = () => {
const config = {
headers: {
'Bearer': getToken()
}
};
axios.get(DRIVER_URL, config)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
};
My expected console log would be something like this
{
"timestamp": 1510038433,
"verb": "GET",
"object": "student",
"data": {
"age": "12",
"id": "90000",
"name": "Test Student",
"emergencyName": "asd",
"createdAt": "2017-10-04T05:39:39+00:00"
}
}
But I keep getting error saying Request failed with status code 400
I am using Expo to develop this app.
Detail on the error is like this
- node_modules/axios/lib/core/createError.js:16:24 in createError
- node_modules/axios/lib/core/settle.js:19:6 in settle
- node_modules/axios/lib/adapters/xhr.js:78:13 in handleLoad
- node_modules/event-target-shim/lib/event-target.js:172:43 in dispatchEvent
- node_modules/react-native/Libraries/Network/XMLHttpRequest.js:540:23 in
setReadyState
- node_modules/react-native/Libraries/Network/XMLHttpRequest.js:381:25 in
__didCompleteResponse
- node_modules/react-native/Libraries/vendor/emitter/EventEmitter.js:182:12 in
emit
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:306:47 in
__callFunction
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:108:26 in
<unknown>
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:269:6 in
__guard
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:107:17 in
callFunctionReturnFlushedQueue
I do not have any authorization to edit the api/server if the error comes from there.
Please help me if there is any point that I have been missing out in the snippet.
Thank you for your help and suggestion.
sidenote, you forgot to return inside getToken
I'll just give you back story on why this happens.
Promises are asynchronous, so is your axios call. Therefore you need to somehow wait for first call result. Otherwise if you put const a = axiosCall() and try to use it right away the a value would be Pending (not a string tho).
For that you can use promises or async/await. I'll show you proper approach with promises. I've just copied your code and refactored it a bit. Also remember that driver is still a promise so you need to handle it as other things.
const getToken = () => {
axios.post(TOKEN_URL, {
email: 'email',
password: 'password',
role: 'user'
})
.then((response) => {
//console.log(response.data.token);
return response.data.token;
})
.catch((error) => {
console.log(error);
});
};
//'export' here is for use in other code: example onPress function
export const fetchDriver = () => {
const config = {
headers: {
'Bearer': getToken()
}
};
axios.get(DRIVER_URL, config)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
};
You are not chaining your requests. You have to wait till you get the token to be able to use it.
Something like this
getToken
const getToken = () => {
return axios.post(TOKEN_URL, {
email: 'email',
password: 'password',
role: 'user'
})
.then((response) => {
//console.log(response.data.token);
return response.data.token;
})
.catch((error) => {
console.log(error);
});
};
fetchDriver
export const fetchDriver = () => {
return getToken().then(token => {
const config = {
headers: {
'Bearer': token
}
};
return axios.get(DRIVER_URL, config)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
});
}
You need to wait until token api get return response and after that You need make second api call with token
change like this
getToken : change to async function
const async getToken = () => {
axios.post(TOKEN_URL, {
email: 'email',
password: 'password',
role: 'user'
})
.then((response) => {
//console.log(response.data.token);
return response.data.token;
})
.catch((error) => {
console.log(error);
});
};
fetchDriver : add await while calling getToken function
export const fetchDriver = () => {
const config = {
headers: {
'Bearer': await getToken()
}
};
axios.get(DRIVER_URL, config)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
};

Categories

Resources