mapStateToProps after form submitting return undefined - javascript

Can you explain me what wrong with my mapStateToProps?
I have Login component form like this.
I want to get state after action send data state to store but it returns undefined (console.log(propReducerState.isAuthenticated)). Here is my Login
import React, {Fragment, useState} from 'react';
import {connect} from 'react-redux';
import {Link, withRouter} from 'react-router-dom';
import {LoginAction} from '../../actions/LoginAction';
import PropTypes from 'prop-types';
const Login = (propAction, propReducerState) => {
const [formData, setFormData] = useState({
email:'',
password:'',
isAuthenticated:null
});
const {email, password} = formData;
const onChange = e => setFormData({...formData, [e.target.name]: e.target.value});
const onSubmit = async e => {
e.preventDefault();
propAction.LoginAction({
email,
password
});
console.log(propReducerState.isAuthenticated);
}
return (
<Fragment>
<h1>Sign In</h1>
<div className="message">{formData.isAuthenticated}</div>
<form className='form' onSubmit={e => onSubmit(e)}>
<div className='form-groum'>
<input
type='text'
placeholder='Please enter your Email'
name='email'
value={email}
onChange={e => onChange(e)} />
<input
type='password'
name='password'
value={password}
onChange={e => onChange(e)} />
</div>
<input type='submit' className='btn btn-primary btn-normal' value='Login' />
</form>
</Fragment>
)
};
const stateStoreToProp = state => ({
propReducerState: state.LoginReducer
})
export default connect(
stateStoreToProp,
{LoginAction}
)(Login);
and here is my LoginReducer:
import {
LOGIN_SUCCESS, LOGIN_FAIL
} from '../actions/typeName';
const initialState = {
token: localStorage.getItem('token'),
isAuthenticated: null,
loading: true,
user: null
}
const LoginReducer = (state = initialState, action) => {
const {type, payload} = action;
switch(type) {
case LOGIN_SUCCESS:
localStorage.setItem('token', payload.token)
return {
...state,
...payload,
isAuthenticated: true,
loading: false
}
case LOGIN_FAIL:
localStorage.removeItem('token')
return {
...state,
token:null,
isAuthenticated: false,
loading: true
}
default:
return {
state
}
}
}
export default LoginReducer;
Thank you for your support.
Update: As I tested with all of your suggestions but no luck. I will add LoginAction for you to check it. Thanks
import axios from 'axios';
import {
LOGIN_SUCCESS, LOGIN_FAIL
} from './typeName';
export const LoginAction = ({email, password}) => async next => {
const config = {
headers: {
'Content-Type': 'application/json'
}
}
const body = JSON.stringify({email, password});
try {
const res = await axios.post('/api/admin/login', body, config);
next({
type: LOGIN_SUCCESS,
payload: res.data
});
}catch(err){
next({
type: LOGIN_FAIL,
});
}
}

This is how you're suppose to connect:
const mapDispatchToProps = dispatch => {
return {
// dispatching plain actions
loginAction: () => dispatch(LoginAction),
}
}
Please make sure that your connect function look like this :
export default connect(
stateStoreToProp,
mapDispatchToProps
)(Login);
and not like this :
export default connect(
null,
{stateStoreToProp, LoginAction}
)(Login);

That depends on how are you using the reducer
You should have something lik this:
export default combineReducers({
test: testReducer
});
And then
const store = createStore(reducer, {});
Where reducer is the result of combineReducers
Then in mapStateToProps the result state of testReducer will be inside test property
Don't use stringify to convert the state to string, keep it as an object

Your connect() has wrong parameter list. The first has to be stateStoreToProp, as the documentation suggests.
You should export it as the following:
export default connect(
stateStoreToProp, {LoginAction}
)(Login);
Read further here: https://react-redux.js.org/api/connect#connect-parameters
I hope that helps!

I solved this problem. Problem is that I put it in form submit
The stateStore always change then I just put it under form submit and everything work well.
Also with that, the prop should be one only. I defined two props and it is not right way.
const Login = ({LoginAction, propReducerState}) => {
const [formData, setFormData] = useState({
email:'',
password:'',
isAuthenticated:null
});
const {email, password} = formData;
const onChange = e => setFormData({...formData, [e.target.name]: e.target.value});
const onSubmit = async e => {
e.preventDefault();
LoginAction({
email,
password
});
}
console.log(propReducerState);
return (
<Fragment>
<h1>Sign In</h1>
<div className="message">{formData.isAuthenticated}</div>
<form className='form' onSubmit={e => onSubmit(e)}>
<div className='form-groum'>
<input
type='text'
placeholder='Please enter your Email'
name='email'
value={email}
onChange={e => onChange(e)} />
<input
type='password'
name='password'
value={password}
onChange={e => onChange(e)} />
</div>
<input type='submit' className='btn btn-primary btn-normal' value='Login' />
</form>
</Fragment>
)
};
const stateStoreToProp = state => ({
propReducerState: state.LoginReducer
})
export default connect(
stateStoreToProp,
{LoginAction}
)(Login);

Related

Cannot destructure property as it is undefined. useContext error

I dont understand why does the below error keeps happening, so I could really use some help!
Error:
Login.jsx:10 Uncaught TypeError: Cannot destructure property 'user' of '(0 , react__WEBPACK_IMPORTED_MODULE_1__.useContext)(...)' as it is undefined.
at Login (Login.jsx:10:1)
Context.js:
import { createContext, useEffect, useReducer } from "react";
import Reducer from "./Reducer";
const INITIAL_STATE = {
user: null,
isFetching: false,
error: false,
};
export const Context = createContext(INITIAL_STATE);
export const ContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(Reducer, INITIAL_STATE);
return (
<Context.Provider
value = {{
user: state.user,
isFetching: state.isFetching,
error: state.error,
dispatch,
}}
>
{children}
</Context.Provider>
);
};
Login.js:
import axios from 'axios';
import { useContext, useRef } from 'react';
import { Link } from 'react-router-dom';
import { Context } from '../../context/Context';
import './login.css';
export default function Login() {
const userRef = useRef();
const passwordRef = useRef();
const { user, dispatch, isFetching } = useContext(Context); //error
const handleSubmit = async (e) => {
e.preventDefault();
dispatch({ type: 'LOGIN_START' });
try {
const res = await axios.post(`http://localhost:5000/api/auth/login`, {
username: userRef.current.value,
password: passwordRef.current.value,
});
dispatch({ type: 'LOGIN_SUCCESS', payload: res.data });
} catch (err) {
dispatch({ type: 'LOGIN_FAILURE' });
console.log(err);
}
};
console.log(user);
return (
<div className='login'>
<span className='loginTitle'>Login</span>
<form className='loginForm' onSubmit={handleSubmit}>
<label>Username</label>
<input
type='text'
className='loginInput'
placeholder='Enter your username...'
ref={userRef}
/>
<label>Password</label>
<input
type='password'
className='loginInput'
placeholder='Enter your password...'
ref={passwordRef}
/>
<button className='loginButton' type='submit' disabled={isFetching}>
Login
</button>
</form>
<button className='loginRegisterButton'>
<Link className='link' to='/register'>
Register
</Link>
</button>
</div>
);
}
Did you wrap the parent of the Login component with the ContextProvider?
import { ContextProvider } from '../../context/Context';
import Login from "..."
function App() {
return(
<>
<ContextProvider>
<Login>
//any other child over here can access the context from this provider
</ContextProvider>
</>
)
}
export default App;

Why is my login component not redirecting successful login's to the right page?

For some reason my Login component is not redirecting a successful login to the right page. I am using a Spring boot backend with a React frontend and am pretty sure I can do this with react on the frontend by using history.push('/profile') to allow a successful login to be redirected to the /profile page but for some reason it stays on /login even after successfully logging in. Any ideas what I am doing wrong? Thank you!
Login.jsx:
import React, {useState, useEffect} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {Link} from 'react-router-dom';
import {Formik, Field, Form, ErrorMessage} from 'formik';
import * as Yup from 'yup';
import {login} from '../../slices/auth';
import {clearMessage} from '../../slices/messages';
const Login = (props) => {
const [loading, setLoading] = useState(false);
const {isLoggedIn} = useSelector((state) => state.auth);
const {message} = useSelector((state) => state.message);
const dispatch = useDispatch();
useEffect(() => {
dispatch(clearMessage());
}, [dispatch]);
const initialValues = {
username: '',
password: '',
};
const validationSchema = Yup.object().shape({
username: Yup.string().required('Please enter your username'),
password: Yup.string().required('Please enter your password'),
});
const handleLogin = (formValue) => {
const {username, password} = formValue;
setLoading(true);
dispatch(login({username, password}))
.unwrap()
.then(() => {
props.history.push('/profile');
window.location.reload();
})
.catch(() => {
setLoading(false);
});
};
if (isLoggedIn) {
return <Link to='/profile' />;
}
return (
<div className='login'>
<div className='card card-container'>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleLogin}
>
<Form>
<div className='form-group'>
<Field
name='username'
type='text'
className='form-control'
placeholder='Username'
/>
<ErrorMessage
name='username'
component='div'
className='alert alert-danger'
/>
</div>
<div className='form-group'>
<Field
name='password'
type='password'
className='form-control'
placeholder='Password'
/>
<ErrorMessage
name='password'
component='div'
className='alert alert-danger'
/>
</div>
<div className='form-group'>
<button
type='submit'
className='btn btn-primary btn-block'
disabled={loading}
>
{loading && (
<span className='spinner-border spinner-border-sm'></span>
)}
<span>Login</span>
</button>
</div>
</Form>
</Formik>
</div>
{message && (
<div className='form-group'>
<div className='alert alert-danger' role='alert'>
{message}
</div>
</div>
)}
</div>
);
};
export default Login;
auth.js:
// We’re gonna import AuthService to make asynchronous HTTP requests with trigger one or more dispatch in the result.
// – register(): calls the AuthService.register(username, email, password) & dispatch setMessage if successful/failed
// – login(): calls the AuthService.login(username, password) & dispatch setMessage if successful/failed
// – logout(): calls the AuthService.logout().
// setMessage is imported from message slice that we’ve created above.
// We also need to use Redux Toolkit createAsyncThunk which provides a thunk that will take care of the action types and dispatching the right actions based on the returned promise.
//There are 3 async Thunks to be exported:
// register
// login
// logout
import {createSlice, createAsyncThunk} from '#reduxjs/toolkit';
import {setMessage} from './messages';
import AuthService from '../services/auth.service';
const user = JSON.parse(localStorage.getItem('user'));
export const register = createAsyncThunk(
'auth/register',
async ({username, email, password}, thunkAPI) => {
try {
const response = await AuthService.register(username, email, password);
thunkAPI.dispatch(setMessage(response.data.message));
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const login = createAsyncThunk(
'auth/login',
async ({username, password}, thunkAPI) => {
try {
const data = await AuthService.login(username, password);
return {user: data};
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const logout = createAsyncThunk('auth/logout', async () => {
await AuthService.logout();
});
const initialState = user
? {isLoggedIn: true, user}
: {isLoggedIn: false, user: null};
const authSlice = createSlice({
name: 'auth',
initialState,
extraReducers: {
[register.fulfilled]: (state, action) => {
state.isLoggedIn = false;
},
[register.rejected]: (state, action) => {
state.isLoggedIn = false;
},
[login.fulfilled]: (state, action) => {
state.isLoggedIn = true;
state.user = action.payload.user;
},
[login.rejected]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
[logout.fulfilled]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
},
});
const {reducer} = authSlice;
export default reducer;
messages.js:
// This updates message state when message action is dispatched from anywhere in the application. It exports 2 action creators:
// setMessage
// clearMessage
import {createSlice} from '#reduxjs/toolkit';
const initialState = {};
const messageSlice = createSlice({
name: 'message',
initialState,
reducers: {
setMessage: (state, action) => {
return {message: action.payload};
},
clearMessage: () => {
return {message: ''};
},
},
});
const {reducer, actions} = messageSlice;
export const {setMessage, clearMessage} = actions;
export default reducer;
Remove the window.location.reload(); from the handleLogin function, it is reloading your app and killing the navigation action.
Since it appears you are using react-router-dom v6, there are no route props and there is no history object. Instead, there is a useNavigate hook.
import { useNavigate } from 'react-router-dom';
...
const Login = (props) => {
const navigate = useNavigate();
...
const handleLogin = (formValue) => {
const { username, password } = formValue;
setLoading(true);
dispatch(login({ username, password }))
.unwrap()
.then(() => {
navigate('/profile', { replace: true });
})
.catch(() => {
setLoading(false);
});
};
...

Redux useReducer and mapDispatchToProps not working

I'm building a react app using redux for state management.
Inside the SearchField component I am using useReducer for handle the search field inputs (cityField and countryField).
When i submit the fetchData function runs and sends a request to an API.
the SearchField component code:
import React, { useReducer } from "react";
import { connect } from 'react-redux';
import { SET_CITY_FIELD, SET_COUNTRY_FIELD } from '../../redux/search-field/search-field.types';
import { reducer, INITIAL_STATE } from "../../redux/search-field/search-field.reducer";
import { fetchData } from '../../redux/weather-api-data/data.actions';
import { SearchFieldContainer, SearchInput, OptionalField, FormComponent } from './search-field.styles';
const SearchField = () => {
const [state, dispatch] = useReducer(reducer, INITIAL_STATE);
const { cityField, countryField } = state;
const onFormSubmit = e => {
e.preventDefault()
cityField.length < 1 ?
alert('Please insert a city')
:
fetchData(cityField, countryField);
}
return (
<SearchFieldContainer>
<FormComponent onSubmit={onFormSubmit}>
<SearchInput type="search" placeholder="Search City" aria-label="Search"
onChange={event => dispatch({ type: SET_CITY_FIELD, payload: event.target.value })}
/>
</FormComponent>
<FormComponent className='country-form' onSubmit={onFormSubmit}>
<SearchInput className='country' type="search" placeholder="Country" aria-label="Search"
onChange={event => dispatch({ type: SET_COUNTRY_FIELD, payload: event.target.value })}
/>
<OptionalField>OPTIONAL</OptionalField>
</FormComponent>
</SearchFieldContainer>
)
}
const mapDispatchToProps = dispatch => ({
fetchData: (cityField, countryField) => dispatch(fetchData(cityField, countryField))
})
export default connect(null, mapDispatchToProps)(SearchField);
The problem is that when the OnFormSubmit function is called, the fetchData function does not send any request and there is no errors in the console. The searchField and countryField data are stored correctly (or at least if I show them in the console I get the actual values). The fetchData function was previously located in the App and worked correctly.
I thank in advance anyone who gives me an answer and tries to find the solution.
The fetchData code:
export const fetchData = (cityField, countryField) => {
return (dispatch) => {
dispatch(fetchCurrentDataRequest())
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${cityField},${countryField}&units=metric&appid=MY_API_KEY`)
.then(response => {
const currentData = response.data
dispatch(fetchCurrentDataSuccess(currentData));
const { lat, lon } = currentData.coord
dispatch(fetchDailyDataRequest())
axios.get(`https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&units=metric&exclude=current,minutely,hourly,alerts&appid=MY_API_KEY`)
.then(response => {
const dailyData = response.data.daily
dispatch(fetchDailyDataSuccess(dailyData))
})
.catch(error => {
const errorMessage = error.message
dispatch(fetchCurrentDataFailure(errorMessage))
})
})
.catch(error => {
const errorMessage = error.message
alert('No results found')
dispatch(fetchCurrentDataFailure(errorMessage))
})
}
I resolved using useDispatch hook instead of mapdispatchToProps, so the new code is:
import React, { useReducer } from "react";
import { useDispatch } from 'react-redux';
import { fetchData } from '../../redux/weather-api-data/data.actions';
import { reducer, INITIAL_STATE } from '../../redux/search-field/search-field.reducer';
import { SET_CITY_FIELD, SET_COUNTRY_FIELD } from '../../redux/search-field/search-field.types';
import { SearchFieldContainer, SearchInput, OptionalField, FormComponent } from './search-field.styles';
const SearchField = () => {
const dispatchData = useDispatch();
const [state, dispatch] = useReducer(reducer, INITIAL_STATE);
const { cityField, countryField } = state;
const onFormSubmit = e => {
e.preventDefault()
cityField.length < 1 ?
alert('Please insert a city')
:
dispatchData(fetchData(cityField, countryField));
}
return (
<SearchFieldContainer>
<FormComponent onSubmit={onFormSubmit}>
<SearchInput type="search" placeholder="Search City" aria-label="Search"
onChange={event => dispatch({ type: SET_CITY_FIELD, payload: event.target.value })}
/>
</FormComponent>
<FormComponent className='country-form' onSubmit={onFormSubmit}>
<SearchInput className='country' type="search" placeholder="Country" aria-label="Search"
onChange={event => dispatch({ type: SET_COUNTRY_FIELD, payload: event.target.value })}
/>
<OptionalField>OPTIONAL</OptionalField>
</FormComponent>
</SearchFieldContainer>
)
}
export default SearchField;

How can i redirect after successful submit of form using react-redux

action.js
import axios from 'axios';
import { EVENT_ADD_FAIL, EVENT_ADD_REQUEST, EVENT_ADD_SUCCESS } from '../constraints/eventConstraint';
const addEvent = (event) => async (dispatch) => {
dispatch({ type: EVENT_ADD_REQUEST, payload: event });
try {
const { data } = await axios.post(`http://localhost:4000/event`, event);
dispatch({ type: EVENT_ADD_SUCCESS, payload:data });
}
catch (error) {
dispatch({ type: EVENT_ADD_FAIL, payload:error.message });
};
};
export { addEvent };
constraint.js
export const EVENT_ADD_REQUEST = 'EVENT_ADD_REQUEST';
export const EVENT_ADD_SUCCESS = 'EVENT_ADD_SUCCESS';
export const EVENT_ADD_FAIL = 'EVENT_ADD_FAIL';
reducer.js
import {EVENT_ADD_FAIL, EVENT_ADD_REQUEST, EVENT_ADD_SUCCESS } from "../constraints/eventConstraint";
function eventAddReducer(state = {}, action) {
switch(action.type) {
case EVENT_ADD_REQUEST:
return { loading: true };
case EVENT_ADD_SUCCESS:
return { loading: false, event: action.payload, success:true };
case EVENT_ADD_FAIL:
return { loading: false, error: action.payload, success:false };
default:
return state
};
};
export { eventAddReducer }
store.js
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { eventAddReducer } from './reducers/eventReducer';
const initialState = {};
const reducer = combineReducers({
addEvent: eventAddReducer
});
const store = createStore(reducer, initialState, compose(applyMiddleware(thunk)));
export default store
event.js
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';
import { addEvent } from '../actions/eventAction';
const AddEvent = () => {
const history = useHistory();
const [event, setEvent] = useState();
const addNewEvent = useSelector(state => state.addEvent);
console.log(addNewEvent)
const dispatch = useDispatch();
const handleChange = e => {
setEvent({ ...event,[e.target.name]:e.target.value})
};
const submitHandler = async (e) => {
e.preventDefault();
await dispatch(addEvent(event));
};
// if(addNewEvent.success === true) {
// history.push('/')
// }; ===========>>>>>>>>>>> It works at first but after submission first time next time it automatically redirects to '/' because react-redux holds state
return (
<>
<form onSubmit = { submitHandler } >
<div className="form-group">
<label htmlFor="name">Name:</label>
<input type="text" className="form-control" id="name" name="name" onChange={e => handleChange(e)} />
</div>
<div className="form-group">
<label htmlFor="description">Description:</label>
<input type="text" className="form-control" id="description" name="description" onChange={e => handleChange(e)} />
</div>
<div className="form-group">
<label htmlFor="price">Price:</label>
<input type="text" className="form-control" id="price" name="price" onChange={e => handleChange(e)} />
</div>
<Link to='/'> <button type="button" className="btn btn-success"> Back </button> </Link>
<button type="submit" className="btn btn-success float-right"> Add Event </button>
</form>
</>
)
};
export default AddEvent
Everything is working fine but I want after successful submission of the form it needs to redirect to some page. It is simple without react-redux we can simply redirect after submission of form but I am trying to learn redux and don't know much about redux. I tried to use success = true in reducer it works at the first time but as redux holds state when I tried to open the link it automatically redirects to the homepage as success = true is hold by react-redux. Any help will be appreciated
First: Make sure you reset success per action:
function eventAddReducer(state = {}, action) {
switch(action.type) {
case EVENT_ADD_REQUEST:
return {
loading: true,
success: null // <-- Look at this
};
/** ... */
};
};
Second: Connect success store-variable to your component, and check for it in componentDidupdate event like:
import { connect } from 'react-redux';
class AddEvent extends React.Component {
componentDidUpdate(prevProps) {
const {success} = this.props;
const {succcess: prevSuccess} = prevProps;
if (success && success !== prevSuccess) {
/** Redirect here */
}
}
/** .... */
}
const mapStateToProps = ({ addEvent: { success } }) => ({
success
});
export default connect(mapStateToProps)(AddEvent);
Using Hooks
const AddEvent = ({ success }) => {
useEffect(() => {
if (success) {
/** Redirect here */
}
}, [success]); // <-- This will make sure that the effect only runs when success variable has changed
};
const mapStateToProps = ({ addEvent: { success } }) => ({
success
});
export default connect(mapStateToProps)(AddEvent);
I ran into the same problem now, and I solved it in two ways
The first: to complete your solution at
event.js file:
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';
import { addEvent } from '../actions/eventAction';
const AddEvent = () => {
const history = useHistory();
const [event, setEvent] = useState();
const addNewEvent = useSelector(state => state.addEvent);
console.log(addNewEvent)
const dispatch = useDispatch();
const handleChange = e => {
setEvent({ ...event,[e.target.name]:e.target.value})
};
const submitHandler = async (e) => {
e.preventDefault();
await dispatch(addEvent(event));
};
addNewEvent.success && history.push('/')
return (
<>
// after submition success only you will redirect to "/"
{addNewEvent.success && history.push('/')}
<form onSubmit = { submitHandler } >
<div className="form-group">
<label htmlFor="name">Name:</label>
<input type="text" className="form-control" id="name" name="name" onChange={e => handleChange(e)} />
</div>
<div className="form-group">
<label htmlFor="description">Description:</label>
<input type="text" className="form-control" id="description" name="description" onChange={e => handleChange(e)} />
</div>
<div className="form-group">
<label htmlFor="price">Price:</label>
<input type="text" className="form-control" id="price" name="price" onChange={e => handleChange(e)} />
</div>
<Link to='/'> <button type="button" className="btn btn-success"> Back </button> </Link>
<button type="submit" className="btn btn-success float-right"> Add Event </button>
</form>
</>
)
};
export default AddEvent
we can only access success value from store reducer after return not before, so you can access value every re-render and redirect based on your condition
now in react-router-dom v6 you can use useNavigate() and make changes for below lines
import { Link, useNavigate } from "react-router-dom";
// rest of imports
const AddEvent = () => {
const navigate = useNavigate();
//rest of code
return (
<>
{addNewEvent.success && navigate('/')}
//rest of code
</>
)
};
export default AddEvent
The second: you can make condition at action.js by sending navigate as an argument on dispatch action and write your condition after dispatch success as below
event.js file
import { Link, useNavigate } from "react-router-dom";
// rest of imports
const AddEvent = () => {
const navigate = useNavigate();
const submitHandler = async (e) => {
e.preventDefault();
await dispatch(addEvent(event,navigate));
};
//rest of code
return (
<>
//rest of code
</>
)
};
export default AddEvent
and at action.js file
import axios from 'axios';
import { EVENT_ADD_FAIL, EVENT_ADD_REQUEST, EVENT_ADD_SUCCESS } from '../constraints/eventConstraint';
const addEvent = (event,navigate) => async (dispatch) => {
dispatch({ type: EVENT_ADD_REQUEST, payload: event });
try {
const { data } = await axios.post(`http://localhost:4000/event`, event);
dispatch({ type: EVENT_ADD_SUCCESS, payload:data });
//add your navigation or condition here
navigate("/");
}
catch (error) {
dispatch({ type: EVENT_ADD_FAIL, payload:error.message });
};
};
export { addEvent };
I know this not the most ideal solution , but how about creating an action that will reset success and dispatch it inside of an useEffect?
Something like this:
Reducer
import {EVENT_ADD_FAIL, EVENT_ADD_REQUEST, EVENT_ADD_SUCCESS } from "../constraints/eventConstraint";
function eventAddReducer(state = {}, action) {
switch(action.type) {
case EVENT_ADD_REQUEST:
return { loading: true };
case EVENT_ADD_SUCCESS:
return { loading: false, event: action.payload, success:true };
case EVENT_ADD_FAIL:
return { loading: false, error: action.payload, success:false };
case RESET:
return {
...state,
loading: false,
success:false
} // This will reset everything including success
default:
return state
};
};
export { eventAddReducer }
and in your event.js file call an action that will dispatch RESET. Make sure you put it inside of an useeffect.
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';
import { addEvent } from '../actions/eventAction';
const AddEvent = () => {
const history = useHistory();
const [event, setEvent] = useState();
const addNewEvent = useSelector(state => state.addEvent);
console.log(addNewEvent)
const dispatch = useDispatch();
React.useEffect(() =>{
myResetAction()
}, [])
const handleChange = e => {
setEvent({ ...event,[e.target.name]:e.target.value})
};
const submitHandler = async (e) => {
e.preventDefault();
await dispatch(addEvent(event));
};
// if(addNewEvent.success === true) {
// history.push('/')
// }; ===========>>>>>>>>>>> It works at first but after submission first time next time it automatically redirects to '/' because react-redux holds state
return (
<>
<form onSubmit = { submitHandler } >
<div className="form-group">
<label htmlFor="name">Name:</label>
<input type="text" className="form-control" id="name" name="name" onChange={e => handleChange(e)} />
</div>
<div className="form-group">
<label htmlFor="description">Description:</label>
<input type="text" className="form-control" id="description" name="description" onChange={e => handleChange(e)} />
</div>
<div className="form-group">
<label htmlFor="price">Price:</label>
<input type="text" className="form-control" id="price" name="price" onChange={e => handleChange(e)} />
</div>
<Link to='/'> <button type="button" className="btn btn-success"> Back </button> </Link>
<button type="submit" className="btn btn-success float-right"> Add Event </button>
</form>
</>
)
)}
Doing this will help.

message in my react app how to solve this

It keeps showing me this message in react, redux app
I have tried to fix it but nothing work and actually I can't know what is the problem in my code
Unhandled Rejection (TypeError): props.setAlerts is not a function
This is my store
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
This is my function
import { SET_ALERT, REMOVE_ALERT } from './types';
import { v4 as uuidv4 } from 'uuid';
export const setAlerts = (masg, alertType) => dispatch => {
const id = uuidv4();
dispatch({
type: SET_ALERT,
payload: { masg, alertType, id }
});
};
this is my reducer
import { SET_ALERT, REMOVE_ALERT } from '../actions/types';
const initialState = [];
export default function(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case SET_ALERT:
return [...state, payload];
case REMOVE_ALERT:
return state.filter(alert => alert.id !== payload);
default:
return state;
}
}
this is my action types
export const SET_ALERT = 'SET_ALERT';
export const REMOVE_ALERT = 'REMOVE_ALERT';
This is my component I want to use my function in
import React, { Fragment, useState } from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { setAlerts } from '../../actions/alert';
export const Register = props => {
const [formData, setFormData] = useState({
name: '',
email: '',
password: '',
password2: ''
});
const { name, email, password, password2 } = formData;
const onChange = e =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmit = async e => {
e.preventDefault();
if (password !== password2) {
props.setAlerts('Password dont match', 'danger');
} else {
console.log('Succes');
}
};
return (
<Fragment>
<section className='container'>
<h1 className='large text-primary'>Sign Up</h1>
<p className='lead'>
<i className='fas fa-user'></i> Create Your Account
</p>
<form className='form' onSubmit={e => onSubmit(e)}>
<div className='form-group'>
<input
type='text'
placeholder='Name'
name='name'
value={name}
onChange={e => onChange(e)}
required
/>
</div>
<div className='form-group'>
<input
type='email'
placeholder='Email Address'
name='email'
value={email}
onChange={e => onChange(e)}
/>
<small className='form-text'>
This site uses Gravatar so if you want a profile image, use a
Gravatar email
</small>
</div>
<div className='form-group'>
<input
type='password'
placeholder='Password'
name='password'
minLength='6'
value={password}
onChange={e => onChange(e)}
/>
</div>
<div className='form-group'>
<input
type='password'
placeholder='Confirm Password'
name='password2'
minLength='6'
value={password2}
onChange={e => onChange(e)}
/>
</div>
<input type='submit' className='btn btn-primary' value='Register' />
</form>
<p className='my-1'>
Already have an account? <Link to='/login'>Sign In</Link>
</p>
</section>
</Fragment>
);
};
export default connect(null, { setAlerts })(Register);
Try this:
const mapDispatchToProps = dispatch => {
return {
setAlerts : (masg, alertType) => { dispatch(setAlerts (masg, alertType)) }
}
}
export default connect(null,mapDispatchToProps)(Register)
When using Redux, do not export this way in the component.
Change below:
export const Register = props => {...}
To this:
const Register = props => {...}
You will need to modify this component's import in the App.js.
Change below:
import { Register } from '...';
To this:
import Register from '...';

Categories