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.
Related
I am sending an image from my reactjs frontend to my nodejs express backend using formData.
But when i am appending the image it doesn't appear in the payload and i get this error from the backend. TypeError: Cannot read properties of undefined (reading 'filename')
I'm sending the data seemingly correct, so i dont understand why the image is not being sent to the backend in the payload.
this is the payload i am getting.
Here is my code:
Backend
user.js:
const MIME_TYPES = {
'image/png': 'png',
'image/jpeg': 'jpg',
'image/jpg': 'jpg',
}
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "images");
},
filename: (req, file, cb) => {
const name = file.originalname.toLowerCase().split(" ").join("-");
const extension = MIME_TYPES[file.mimetype];
cb(null, name + "-" + Date.now() + "." + extension);
}
})
router.post("/register", multer({ storage: storage }).single("uniIDImage"), (req, res, next) => {
const url = req.protocol + "://" + req.get("host");
const fullUrl = url + "/images/" + req.file.filename;
let User = new models.User();
User.register(req.body, fullUrl).then((response) => {
console.log(response);
if(response) {
return res.json({
msg: 'User registered successfully.',
statusCode:200,
result: response,
});
}
});
});
Reactjs frontend:
registerActions.js:
export const register = (formData) => async dispatch => {
const config = {
headers: {
'Content-Type': 'multipart/form-data;',
}
}
let jsonObject = {};
for (let key of formData.keys()) {
jsonObject[key] = formData.get(key);
console.log( jsonObject[key])
}
try {
const res = await axios.post('/register', jsonObject, config);
dispatch({
type: REGISTER_SUCCESS,
payload: res.data
});
} catch (err) {
dispatch({
type:REGISTER_FAIL,
});
}
}
Register.js
import React, { useEffect, useReducer, useState } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { register } from '../../store/auth/register/actions'
const Register = ({ register }) => {
const [uniIDImage, setUniIDImage] = useState([]);
const [data, setData] = useState({
name: '',
uniID: '',
username: '',
email: '',
phoneNumber: '',
password: '',
});
const handleChange = (e) => {
setData({ ...data, [e.target.name]: e.target.value });
}
const handleImage = (e) => {
setUniIDImage({ pic: e.target.files[0] });
}
const signUp = (e) => {
e.preventDefault();
const formData = new FormData()
formData.append("name", data.name);
formData.append("uniID", data.uniID);
formData.append("username", data.username);
formData.append("email", data.email);
formData.append("phoneNumber", data.phoneNumber);
formData.append("uniIDImage", uniIDImage.pic);
formData.append("password", data.password);
console.log(uniIDImage.pic);
register(formData);
}
return (
<div className="mb-3">
<Label className="form-label">ID Photo</Label>
<Input
id="fileInput"
name="uniIDImage"
className="form-control"
accept="image/*"
type="file"
onChange={handleImage}
/>
)
Register.propTypes = {
register: PropTypes.func.isRequired,
}
export default connect(null, { register })(Register);
When you convert form data to JSON object, you lose the file, and there is no point converting it, and also no need to set content-type header.
So, simply pass formData to axios as a second argument:
export const register = (formData) => async dispatch => {
try {
const res = await axios.post('/register', formData);
dispatch({
type: REGISTER_SUCCESS,
payload: res.data
});
} catch (err) {
dispatch({
type: REGISTER_FAIL,
});
}
}
how to write a method to return async storage value in react native.
I have done login authentication using Context API.
AuthContext.js
import React, { createContext } from "react";
import { useState, useEffect } from "react";
import { Alert } from "react-native";
import AsyncStorage from '#react-native-async-storage/async-storage';
import NetInfo from "#react-native-community/netinfo";
import { BASE_URL } from "../constants/Const";
export const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
// loading & usertoken hooks
const [isLoading, setIsLoading] = useState(false);
const [userToken, setuserToken] = useState(null);
//login method
const login = async (email, password) => {
setIsLoading(true);
//fetch method to get access token
fetch(`${BASE_URL}/sign_in`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: email,
password: password,
})
})
.then((res) => res.json())
.then((json) => {
//console.log("login auth ", json)
if (json.responseCode == 200) {
try {
setuserToken(json.responseData.access_token);
//storing usertoken value in react-native encrypted storage
AsyncStorage.setItem("userToken", json.responseData.access_token)
//console.log("user token",userToken);
}
catch (error) {
console.log("Error while saving user token data", userToken);
}
}
//showing invalid email & password alert messaqge
else {
Alert.alert('Invalid email or password.');
return;
}
})
.catch((error) => {
console.error(error);
});
setIsLoading(false);
}
//logout method
const logout = async () => {
setIsLoading(true);
//setting user token as null
setuserToken(null);
try {
//removing usertoken value in react-native encrypted storage
await AsyncStorage.removeItem("userToken");
} catch (error) {
// There was an error on the native side
console.log("Error while removing data", error);
}
setIsLoading(false);
}
// checking user is already logged in each time when app starts
const isLoggenIn = async () => {
try {
setIsLoading(true);
//let userToken = await EncryptedStorage.getItem("userToken");
let userToken = await AsyncStorage.getItem("userToken");
//console.log("accesed user token",userToken);
setuserToken(userToken);
setIsLoading(false);
} catch (error) {
console.log("Error retrieving data", error);
}
}
const [connected, setConnected] = useState(true);
useEffect(() => {
isLoggenIn();
// Subscribe
const unsubscribe = NetInfo.addEventListener(state => {
// console.log("Connection type", state.type);
// console.log("Is connected?", state.isConnected);
setConnected(state.isConnected);
});
//clean up function
return () => unsubscribe();
}, [])
return (
<AuthContext.Provider value={{ login, logout, isLoading, userToken ,connected,}}>
{children}
</AuthContext.Provider>
)
}
in Redux slice file I want access token values to make a fetch request to a server.i defined getAccessToken Method to return accessToken value but it is not returning value
DataSlice.js
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit'
import { useState } from 'react';
import { Alert } from 'react-native';
import { BASE_URL } from '../constants/Const';
import AsyncStorage from '#react-native-async-storage/async-storage';
//const accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNjM2YTFlNTc4YWNlNTRjM2E5OWE4YWI2IiwiZW1haWwiOiJhZG1pbkBnbWFpbC5jb20iLCJpYXQiOjE2Njg0MDY2MzgsImV4cCI6MTY2ODQ2MDYzOH0.i_GHy2K91I0_159AIpQ4m2eFUmyXYFmF3_0sQ-o_x-w";
//user token function
const getAcessToken = async () => {
const token = await AsyncStorage.getItem('userToken');
return token;
}
var userToken = getAcessToken();
// get and delete method request options
const getDeleteRequestOptions = (methodType) => {
return {
method: methodType,
headers: {
'Content-Type': 'application/json',
'autherization': userToken
}
}
}
// save and edit method request options
const saveEditRequestOptions = (methodType, data) => {
console.log("img uri",data.imgUri)
const imgData = new FormData();
imgData.append('first_name', data.firstName);
imgData.append('last_name', data.lastName);
imgData.append('phone', data.phoneNo);
imgData.append('email', data.email);
imgData.append('image', { uri: data.imgUri, name: 'image', type: 'image/jpg' });
return {
method: methodType,
headers: {
'Content-Type': 'multipart/form-data',
'autherization': userToken
},
body: imgData
}
};
// fetch data
export const getData = createAsyncThunk('fetch', async ({pageNo,limit}) => {
return fetch(`${BASE_URL}/list_profile?page_number=${pageNo}&limit=${limit}`, getDeleteRequestOptions('GET'))
.then((response) => response.json())
.then((json) => {
//returning json receive array
if(pageNo === 0 && limit === 0){
return {data:json.receive,fetchAllData:true};
}
return {data:json.receive,fetchAllData:false};
})
.catch((error) => {
console.error(error);
});
});
// delete data
export const deleteData = createAsyncThunk('delete', async ({id}) => {
return fetch(`${BASE_URL}/delete_profile/${id}`, getDeleteRequestOptions('DELETE',userToken))
.then((res) => res.json())
.catch((error) => {
console.error(error);
});
});
// save data
export const saveData = createAsyncThunk('save', async (data) => {
return fetch(`${BASE_URL}/add_profile`, saveEditRequestOptions('POST', data))
.then((res) => res.json())
.then((json) => {
if (json.responseCode === 211) {
Alert.alert('Input Error', json.responseMessage, [
{ text: "OK" }
])
return;
}
console.log("save responese message ", json.responseMessage);
})
.catch((error) => {
console.error(error);
});
});
// edit data
export const editData = createAsyncThunk('edit', async (data) => {
return fetch(`${BASE_URL}/update_profile/${data.id}`, saveEditRequestOptions('PUT', data))
.then((res) => res.json())
.then((json) => {
console.log("edit data response message ", json.responseMessage);
})
.catch((error) => {
console.error(error);
});
});
const initialState = {
masterData: [],
filteredData: [], //array to implement search
allData:[],
imgurl: '',
};
export const dataSlice = createSlice({
name: 'crud',
initialState,
reducers: {
filterData: (state, action) => {
state.filteredData = action.payload;
},
selectedImage: (state, action) => {
state.imgurl = action.payload;
},
},
extraReducers: {
// get data
[getData.pending]: (state, action) => {
console.log('fetching data is pending');
},
[getData.fulfilled]: (state, action) => {
console.log('data fetched successfully')
if (!action.payload) {
Alert.alert('Network error', 'Data Fetching is Failded Please try Again later.', [
{ text: "OK" }
])
return;
}
console.log(action.payload.fetchAllData)
if(action.payload.fetchAllData){
//console.log("inside fetch all data")
state.allData = action.payload.data;
}
state.masterData = action.payload.data;
state.filteredData = action.payload.data;
},
[getData.rejected]: (state, action) => {
console.log('fetching request rejected');
},
// delete data
[deleteData.pending]: (state, action) => {
console.log('delete data is pending');
},
[deleteData.fulfilled]: (state, action) => {
console.log('data deleted successfully');
},
[deleteData.rejected]: (state, action) => {
console.log('data delete request rejected');
Alert.alert('Delete Data Failure', 'Deleting Data Failed. Please try Again later.', [
{ text: "OK" }
])
},
// save data
[saveData.pending]: (state, action) => {
console.log('saving data is pending');
},
[saveData.fulfilled]: (state, action) => {
console.log('data saved successfully');
},
[saveData.rejected]: (state, action) => {
console.log('data save request rejected');
Alert.alert('Data Save Failure', 'Data Save Failed. Please try Again later.', [
{ text: "OK" }
])
},
//edit data
[editData.pending]: (state, action) => {
console.log('edit data is pending');
},
[editData.fulfilled]: (state, action) => {
console.log('data edited successfully');
},
[editData.rejected]: (state, action) => {
console.log('edit data request rejected');
Alert.alert('Data Edit Failure', 'Edit data Failed. Please try Again later.', [
{ text: "OK" }
])
},
},
})
// Action creators are generated for each case reducer function
export const { filterData, selectedImage } = dataSlice.actions;
export default dataSlice.reducer
i want to get access token values
The reason is that you are not calling getAccessToken() with await like this:
var userToken = await getAcessToken();
since it is asynchronous function.
So basically as the APP.js renders it is not sending requests to the backend. I am calling the currentUser function inside App.js function. Please help me I am stuck
app.js file
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async (user) => {
if (user) {
const getidtoken = await user.getIdTokenResult();
currentUser(getidtoken.token)`enter code here`
.then((res) => {
console.log(res);
dispatch({
type: 'LOGGED_IN_USER',
payload: {
email: res.data.email,
name: res.data.name,
role: res.data.role,
_id: res.data._id,
},
});
})
.catch((err) => {
console.log(err);
});
}
});
currentuser.js Function
export const currentUser = async (authtoken) => {
return await axios.post(
process.env.REACT_APP_API_USER,
{},
{ headers: { authtoken: authtoken } }
);
};
enter image description here
I'm trying to delete an user, but the req.body in the backend is an empty object.
In the backend I have the following code:
const deleteUser = async (req, res) => {
console.log(req.body);
console.log(req.config);
const user = await User.findById(req.body.userId);
if (user) {
const deleteUser = await user.remove();
res.send({ message: "User Deleted", user: deleteUser });
} else {
res.status(404).send({ message: "User Not Found" });
}
};
Here the console log is an empty object, I must that the other functions work perfectly.
In the frontend, I'm using redux, I think I'm doing something wrong in the actions, but I can't find out what, I will post all my code for reference.
action.js:
export const deleteUser = (userId) => async (dispatch, getState) => {
dispatch({ type: USER_DELETE_REQUEST, payload: userId });
try {
const { data } = await Axios.delete(
"http://localhost:3030/v1/user/userProfile/deleteUser",
{
userId: userId,
}
);
dispatch({ type: USER_DELETE_SUCCESS, payload: data });
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: USER_DELETE_FAIL, payload: message });
}
};
In the reducer:
export const userDeleteReducer = (state = {}, action) => {
switch (action.type) {
case USER_DELETE_REQUEST:
return { loading: true };
case USER_DELETE_SUCCESS:
return { loading: false, success: true };
case USER_DELETE_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
And I'm calling the action like that:
const userSignin = useSelector((state) => state.userSignin);
const { userInfo, loading, error } = userSignin;
<button
onClick={() => {
console.log(userInfo._id);
dispatch(deleteUser(userInfo._id));
props.onClose();
}}
className='deleteAccountModalButton'
>
Delete account!
</button>
I tried everything, but I can't find where the problem, can somebody tell me why the req.body is empty in the backend?
EDIT:
I managed to make it work by modifying the order of parameters in actions:
export const deleteUser = (userId) => async (dispatch, getState) => {
dispatch({ type: USER_DELETE_REQUEST, payload: userId });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.delete(
"http://localhost:3030/v1/user/userProfile/deleteUser",
{
data: {
headers: { Authorization: `Bearer ${userInfo.token}` },
userId,
},
}
);
dispatch({ type: USER_DELETE_SUCCESS, payload: data });
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: USER_DELETE_FAIL, payload: message });
}
};
I will leave this here in case somebody else will have this problem.
I have a button that dispatches an action to create a post, for some reason the request never proceeds and it fails. This is the action. I have constants that's why types is not on a string
export const createPost = () => async (dispatch, getState) => {
try {
dispatch({
type: POST_CREATE_REQUEST,
});
const {
userLogin: { userInfo },
} = getState();
const config = {
headers: {
Authorization: `Bearer ${userInfo.token}`,
},
};
const { data } = await axios.post(
`http://localhost:5000/api/posts`,
config
);
dispatch({
type: POST_CREATE_SUCCESS,
payload: data,
});
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
// if (message === 'Not authorized, token failed') {
// dispatch(logout());
// }
dispatch({
type: POST_CREATE_FAIL,
payload: message,
});
}
};
It continues to the POST_CREATE_REQUEST but always errors out to the POST_CREATE_FAIL.
I tried using postman and it works fine, I think the problem is the createPost action can't seem to receive the token even though im logged in as an admin, I'm not sure.
This is the useSelector of the postCreate
const postCreate = useSelector(state => state.postCreate);
const {
loading: loadingCreate,
error: errorCreate,
success: successCreate,
post: createdPost,
} = postCreate;
and this is the useSelector of the user that is logged in, currently as an admin.
const userLogin = useSelector(state => state.userLogin);
const { userInfo } = userLogin;
Rewrite this code
const { data } = await axios.post(
`http://localhost:5000/api/posts`,
config
);
as
const res = await axios.post(
`http://localhost:5000/api/posts`,
config
);
const data = res && res.data
There is already values on my Controller at the backend and just needed to add brackets in the action
from this
const { data } = await axios.post(
`http://localhost:5000/api/posts`,
config
);
to this
const { data } = await axios.post(
`http://localhost:5000/api/posts`, {},
config
);