I'm trying to call a function from React JSX button and I have a problem with that because when in react-redux actions in the defined function I don't put dispatch function, works as it supposed to.
const like_function = (id) => {
let post_id = id;
if (isAuthenticated) {
console.log(post_id, user_id);
like_post(post_id, user_id);
} else {
<Redirect to="/login" />;
console.log("Redirect to login");
}
};
Here in this button I invoke function with one parameter.
<button onClick={() => like_function(post.id)}>Like</button>
This is redux action. Here is the problem. When dispatch is deleted function works but with dispatch is not even called, it wont even log data to console before async request
export const like_post = (post_id, user_id) => async (dispatch) => {
const data = { post_id: post_id, user_id: user_id };
console.log(data);
dispatch({
type: POST_LIKE_LOADING,
});
try {
const res = await axios.put(`http://localhost:8000/api/like_list/`, data);
//console.log(res.data);
dispatch({
type: POST_LIKED,
payload: res.data,
});
} catch (err) {
console.log(err);
dispatch({
type: POST_LIKEING_FAIL,
});
}
};
Here are my redux reducers
case POST_LIKE_LOADING:
return {
...state,
isLoading: true,
};
case POST_LIKED:
return {
...state,
isLoading: true,
message: "OK"
};
Sorry about my English, hope you understood me, thanks in advance
You're not dispatching the action.
import { useDispatch } from 'react-redux'
const dispatch = useDispatch()
dispatch(like_post(post_id, user_id))
Related
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.
How can I use the current status of redux after the thunks and actions have finished? The problem is in the handleSubmit function if I register a user with errors, it updates the status of redux with the message "Email already registered", but when accessing the state in the dispatch promise sends me a wrong state, without the message.
Function hanldeSubmit
const handleSubmit = (e) => {
e.preventDefault()
const form = {
name: e.target[0].value,
email: e.target[1].value,
password: e.target[2].value,
confirmPassword: e.target[3].value
}
const { name, email, password } = form
if (isFormValid(form)) {
//TODO: FIX IT synchronize redux with errors
dispatch( startRegisterUser(name, email, password) ).then(() => {
console.log(state)
})
}
}
register action and thunk
export const startRegisterUser = (name, email, password) => {
return (dispatch, state) => {
dispatch(startLoadingAction())
return firebase.auth().createUserWithEmailAndPassword(email, password)
.then(async ({ user }) => {
await user.updateProfile({
displayName: name,
photoURL: ''
})
dispatch(registerUserAction(user.uid, user.displayName))
})
.catch(e => {
if (e.code === "auth/email-already-in-use") {
dispatch(errorAction("Email already registered"))
} else {
dispatch(errorAction("Unexpected error"))
}
})
.then(() => {
dispatch(finishLoadingAction())
console.log("finished dispatch's", state())
return
})
}
}
export const registerUserAction = (uid, displayname) => {
return {
type: types.register,
payload: {
uid,
displayname
}
}
}
console logs
I want to get the status of the first console log but in the handlesubmit function
You should handle the errorAction in the reducer, update the ui store slice with the error message. And, you need to return the state() in the promise in the thunk function. Then, you will get the whole state inside the handleSubmit event handler.
E.g.
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
function errorAction(message) {
return {
type: 'ERROR',
payload: message,
error: true,
};
}
export const startRegisterUser = (name, email, password) => {
return (dispatch, state) => {
return Promise.reject({ code: 'auth/email-already-in-use' })
.catch((e) => {
if (e.code === 'auth/email-already-in-use') {
dispatch(errorAction('Email already registered'));
} else {
dispatch(errorAction('Unexpected error'));
}
})
.then(() => state());
};
};
export const registerUserAction = (uid, displayname) => {
return {
type: 'REGISTER',
payload: {
uid,
displayname,
},
};
};
function rootReducer(state = { ui: { error: '' } }, action) {
switch (action.type) {
case 'ERROR':
return { ui: { error: action.payload } };
default:
return state;
}
}
const store = createStore(rootReducer, applyMiddleware(thunk));
function handleSubmit() {
store
.dispatch(startRegisterUser('name', 'example#gmail.com', '123') as any)
.then((state) => {
console.log('handleSubmit state: ', state);
});
}
// triggered by user submit event
handleSubmit();
Output:
handleSubmit state: { ui: { error: 'Email already registered' } }
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
);
I'm trying to update my user profile data from the client-side. The user has to upload a file and the component will catch the actual user-loaded ID, store it into a state, and then use this state to find the user in the database to update the value I need. But I can't figure out how to pass the state to filter the user; in the way you see below the It gives me a PUT: http://localhost:3000/api/users/upgrade/undefined 404 (Not Found). Someone could help me?
Here's my server router:
//SERVER ROUTER
router.put("/upgrade/:id", upgrade.single("userPlus_doc"), (req, res) => {
User.findById(req.params.id)
.then((user) => {
user.userPlus = true;
user.userPlus_doc = req.file.originalname;
user
.save()
.then(() => res.json("User Upgraded!"))
.catch((err) => res.status(404).json({ success: false }));
})
.catch((err) => res.status(404).json({ success: false }));
});
My action and reducer:
//ACTION
export const upgradeUser = (formData, id) => (dispatch, getState) => {
axios
.put(`/api/users/upgrade/${id}`, formData, tokenConfig(getState))
.then((res) =>
dispatch({
type: USER_UPGRADE,
payload: res.data,
})
)
.catch((err) =>
dispatch(returnErrors(err.response.data, err.response.status))
);
};
export const setUserUpgradeID = (user_id) => (dispatch) => {
dispatch({
type: SET_USER_UPGRADE_ID,
payload: user_id,
});
};
//REDUCER
const initialState = {
user_id: "",
};
export default function foo(state = initialState, action) {
switch (action.type) {
...
case SET_USER_UPGRADE_ID:
return {
...state,
user_id: action.payload,
};
case USER_UPGRADE:
return {
...state,
user: state.user.filter((user) => user._id !== action.payload),
};
default:
return state;
}
}
And how I pass the information client-side:
class ProfileUpgrade extends Component {
state = {
userPlus_doc: "",
user_id: "",
};
onFileChange = (e) => {
this.setState({
userPlus_doc: e.target.files[0],
});
this.props.setUserUpgradeID({
user_id: this.props.auth.user._id,
});
};
/* onChange = () => {
this.props.setUserUpgradeID({
user_id: this.props.auth.user._id,
});
console.log(this.props.setUserUpgradeID);
};
*/
onSubmit = (e, user_id) => {
e.preventDefault();
const formData = new FormData();
/* formData.append("userPlus", this.state.userPlus); */
formData.append("userPlus_doc", this.state.userPlus_doc);
this.props.upgradeUser(formData, this.props.user_id);
};
render() {
return ( ... )
}
}
ProfileUpgrade.propTypes = {
auth: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
user: state.user,
auth: state.auth,
user_id: state.user_id,
});
export default connect(mapStateToProps, { upgradeUser, setUserUpgradeID })(
ProfileUpgrade
);
The only things I need from the client-side are the doc title and the user ID to update the correct user object. To update the boolean userPlus I set the backend to set the value to true and save it.
Am I missing something in my action to make the component pass filter the user ID?
i am using redux in react-native to fetch data from an api, here is whhat i have done so far
api_type.js
export const USER_LOGIN = 'user_login_action';
export const USER_LOGINING = 'logining_users';
export const USER_LOGEDIN = 'user_logged_in';
index.js
import axios from 'axios';
import { USER_LOGIN, USER_WALLETS,USER_LOGINING } from './api_types';
const AUTH_API_URL = 'http:/api/v1';
const CORE_API_URL = 'http:/api/v1';
let username="";
let password="";
let auth_token ="";
let AuthStr = "";
export function UserWallets(){
return function(dispatch){
AuthStr ="Bearer "+auth_token;
console.log ("new auth : "+AuthStr);
axios.defaults.headers.common['Authorization'] = AuthStr
axios.get(`${CORE_API_URL}/wallet/allwallets`)
.then(response => {
dispatch({
type: USER_WALLETS,
payload: response['data']
});
}).catch((error) => {
console.log(error);
})
}
}
export function UserLogin() {
return function(dispatch) {
dispatch({
type:USER_LOGINING
});
axios.post(
`${AUTH_API_URL}/authenticate/users`,
{
email: username,
password: password
}
)
.then(response => {
dispatch({
type: USER_LOGIN,
payload: response['data']
});
auth_token=response['data']['token'];
}
)
.catch((error) => {
console.log(error);
})
}
}
export function username(term) {
username=term;
console.log("username " +username);
return{
type:"username",
username
};
}
export function password(term) {
password=term;
console.log("password " +password);
return{
type:"password",
password
};
}
export function authToken (term){
auth_token = term;
return{
type:"authtoken",
auth_token
}
}
auth_reducer.js
import { USER_LOGIN ,USER_LOGINING } from '../actions/api_types';
const INTIAL_STATE = {
message: '',
token:'',
logging: false,
loggedin: false,
loginerr: null,
};
export default function (state = INTIAL_STATE, action) {
console.log("present state"+action.type);
switch(action.type) {
case USER_LOGIN:{
return { ...state, message: action.payload.message, token:action.payload.token,loggedin:true};
}
case USER_LOGINING:{
return {...state,logging:true }
}
default:{
console.log("default "+action.type);
}
}
return state;
}
index.js // combine reducer
import { combineReducers } from 'redux';
import drawer from './drawer';
import AuthReducer from './auth_reducer';
import CoreReducer from './core_reducer';
export default combineReducers({
auth: AuthReducer,
});
i have created and configured the store and wrapped my app with the provider from react-redux, and i have passed the store to the provider, in a nutshell i can now access the store from my componets.
below is a function in my login_component, that triggers once i click on login
login(){
if(this.state.email==""){
alert("Email require");
return;
}else if(this.state.password==""){
alert("password require");
return;
}else{
//set the paramter for the reducer to use
this.props.username(this.state.email);
this.props.password(this.state.password);
//activate the user login action
this.props.UserLogin();
if(!this.props.auth.loggedin){
console.log("logging in");
//show loadging gif
}
//checking from response from the auth api
if(this.props.auth.message=="user successfully logged in"){
alert(this.props.auth.token);
Actions.home();
}else{
alert("invalid Username/Password");
}
}
}
Now this is problem, once i click on login, the block of code i commented (check response from api) will not wait for the store value to change before it perform it action, please i need a way around this.
i finally got a solution to the problem, the api call was async but the problem was that in the component, i tested for the response before the store changes so here is the solution, i added the following to my login component
componentWillReceiveProps(nextProps) {
console.log("component update");
if(nextProps.auth.loggedin==true){
if(nextProps.auth.message=="user successfully logged in"){
this.setState(previousState => {
return { spinnerv: false };
});
Actions.home();
}else{
alert("invalid Username/Password");
}
}
}
what happens here is that function componentWillReceiveProps, check if the states has changed and then text if the response is componentWillReceiveProps.
thanks jmargolisvt for your support.
i hope this help someone else.
You need to perform this API call asynchronously. Basically, you will have your login function dispatch an async action that will make the API call. Then from your success/fail methods of the API call, you'll dispatch another (synchronous) call that either logs the user in or not.
You'll want to incorporate Redux Thunks to make your async call.
https://github.com/gaearon/redux-thunk