Why reducer is not being activated after dispatch() - redux

When I submit my SignIn Form validateAndSignInUser is called and this dispatch signInUser that sends to backend an email and password to get a session token. That works right.
After signInUser returns values signInUserSuccess is dispatched, and I verified this works.
But after that the UserReducer is not beign activated and I don't understand why. What is wrong?
I have this action in my SignInFormContainer.js:
import { reduxForm } from 'redux-form';
import SignInForm from '../components/SignInForm';
import { signInUser, signInUserSuccess, signInUserFailure } from '../actions/UsersActions';
const validateAndSignInUser = (values, dispatch) => {
return new Promise ((resolve, reject) => {
let response = dispatch(signInUser(values));
response.payload.then((payload) => {
// if any one of these exist, then there is a field error
if(payload.status != 200) {
// let other components know of error by updating the redux` state
dispatch(signInUserFailure(payload));
reject(payload.data); // this is for redux-form itself
} else {
// store JWT Token to browser session storage
// If you use localStorage instead of sessionStorage, then this w/ persisted across tabs and new windows.
// sessionStorage = persisted only in current tab
sessionStorage.setItem('dhfUserToken', payload.data.token);
// let other components know that we got user and things are fine by updating the redux` state
dispatch(signInUserSuccess(payload));
resolve(); // this is for redux-form itself
}
}).catch((payload) => {
// let other components know of error by updating the redux` state
sessionStorage.removeItem('dhfUserToken');
dispatch(signInUserFailure(payload));
reject(payload.data); // this is for redux-form itself
});
});
}
const mapDispatchToProps = (dispatch) => {
return {
signInUser: validateAndSignInUser /*,
resetMe: () => {
// sign up is not reused, so we dont need to resetUserFields
// in our case, it will remove authenticated users
// dispatch(resetUserFields());
}*/
}
}
function mapStateToProps(state, ownProps) {
return {
user: state.user
};
}
// connect: first argument is mapStateToProps, 2nd is mapDispatchToProps
// reduxForm: 1st is form config, 2nd is mapStateToProps, 3rd is mapDispatchToProps
export default reduxForm({
form: 'SignInForm',
fields: ['email', 'password'],
null,
null,
validate
}, mapStateToProps, mapDispatchToProps)(SignInForm);
This three actions in UserActions.js
import axios from 'axios';
//sign in user
export const SIGNIN_USER = 'SIGNIN_USER';
export const SIGNIN_USER_SUCCESS = 'SIGNIN_USER_SUCCESS';
export const SIGNIN_USER_FAILURE = 'SIGNIN_USER_FAILURE';
export function signInUser(formValues) {
const request = axios.post('/login', formValues);
return {
type: SIGNIN_USER,
payload: request
};
}
export function signInUserSuccess(user) {
console.log('signInUserSuccess()');
console.log(user);
return {
type: SIGNIN_USER_SUCCESS,
payload: user
}
}
export function signInUserFailure(error) {
console.log('signInUserFailure()');
console.log(error);
return {
type: SIGNIN_USER_FAILURE,
payload: error
}
}
And this is my reducer UserReducer.js
import {
SIGNIN_USER, SIGNIN_USER_SUCCESS, SIGNIN_USER_FAILURE,
} from '../actions/UsersActions';
const INITIAL_STATE = {user: null, status:null, error:null, loading: false};
export default function(state = INITIAL_STATE, action) {
let error;
switch(action.type) {
case SIGNIN_USER:// sign in user, set loading = true and status = signin
return { ...state, user: null, status:'signin', error:null, loading: true};
case SIGNIN_USER_SUCCESS://return authenticated user, make loading = false and status = authenticated
return { ...state, user: action.payload.data.user, status:'authenticated', error:null, loading: false}; //<-- authenticated
case SIGNIN_USER_FAILURE:// return error and make loading = false
error = action.payload.data || {message: action.payload.message};//2nd one is network or server down errors
return { ...state, user: null, status:'signin', error:error, loading: false};
default:
return state;
}
}
Reducers combined:
import { combineReducers } from 'redux';
import { UserReducer } from './UserReducer';
import { reducer as formReducer } from 'redux-form';
const rootReducer = combineReducers({
user: UserReducer,
form: formReducer // <-- redux-form
});
export default rootReducer;
configureStore.js
import {createStore, applyMiddleware, combineReducers, compose} from 'redux';
import thunkMiddleware from 'redux-thunk';
import {devTools, persistState} from 'redux-devtools';
import rootReducer from '../reducers/index';
let createStoreWithMiddleware;
// Configure the dev tools when in DEV mode
if (__DEV__) {
createStoreWithMiddleware = compose(
applyMiddleware(thunkMiddleware),
devTools(),
persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
)(createStore);
} else {
createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
}
export default function configureStore(initialState) {
return createStoreWithMiddleware(rootReducer, initialState);
}
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import { Router, browserHistory } from 'react-router';
import routes from './routes';
import configureStore from './store/configureStore';
import {renderDevTools} from './utils/devTools';
const store = configureStore();
ReactDOM.render(
<div>
{/* <Home /> is your app entry point */}
<Provider store={store}>
<Router history={browserHistory} routes={routes} />
</Provider>
{/* only renders when running in DEV mode */
renderDevTools(store)
}
</div>
, document.getElementById('main'));

Related

useEffect hook problem, use redux to get data from backend to frontend

Hello i am new to programming, and i have learn to use MERN to make a sign up,i have no issue with backend but when i tried to use redux ,i have this problem in the frontend
enter image description here
This is the code for the SignInScreen.js
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
export const SignInScreen = (props, history) => {
const navigate = useNavigate
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'
const userlogin = useSelector((state) => state.userlogin)
const { userInfo, loading, error } = userlogin
const dispatch = useDispatch()
useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])
I dont know what wrong with the code,but i do know that it connected with the redux store which have reducer,action and constant..this is for the redux store.js
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import {
userLoginReducer,
} from './reducers/userReducer'
const userInfo = localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null
const initialState = {
userLogin: { userInfo },
}
const reducer = combineReducers({
userLogin: userLoginReducer,
})
const middleware = [thunk]
const store = createStore(
reducer,
initialState,
compose(applyMiddleware(...middleware))
)
export default store
This is for constant
export const USER_LOGIN_REQUEST = 'USER_LOGIN_REQUEST'
export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS'
export const USER_LOGIN_FAIL = 'USER_LOGIN_FAIL'
export const USER_LOGOUT = 'USER_LOGOUT'
this is userReducer.js
import {
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGIN_FAIL,
USER_LOGOUT,
} from '../constants/userConstant'
function userLoginReducer(state = {}, action) {
switch (action.type) {
case USER_LOGIN_REQUEST:
return { loading: true }
case USER_LOGIN_SUCCESS:
return { loading: false, userInfo: action.payload }
case USER_LOGIN_FAIL:
return { loading: false, error: action.payload }
case USER_LOGOUT:
return {}
default:
return state
}
}
export userLoginReducer
and lastly for user.js
import Axios from 'axios'
import {
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGIN_FAIL,
} from '../constants/userConstant'
const login = (email, password) => async (dispatch) => {
try {
dispatch({ type: USER_LOGIN_REQUEST })
const config = { headers: { 'Content-Type': 'application/json' } }
const { data } = await Axios.post(
'/api/users/login',
{ email, password },
config
)
dispatch({ type: USER_LOGIN_SUCCESS, payload: data })
localStorage.setItem('userInfo', JSON.stringify(data))
} catch (error) {
dispatch({
type: USER_LOGIN_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
})
}
}
export login
I just want to get the data into the userInfo but it dont recognized them and said it is a TypeError..I hope u can help me with this..im using the redux latest version
The problem is in userLoginReducer. Each reducer should return a complete new copy of the store.
If you return just the changes, the object you return replaces the entire state.
For example in this code:
switch (action.type) {
case USER_LOGIN_REQUEST:
return { loading: true };
}
The state { userLogin: { userInfo } } will be replaced with { loading: true }. Then you will not have userLogin anymore in the state. That's why you get the error.
To overcome this problem, spread the previous state in returned object (for all actions):
switch (action.type) {
case USER_LOGIN_REQUEST:
return { ....state, loading: true }; // ...state copies exist state to the new copy of state
}
Note: To easily solve similar bugs in the future, I recommend to use redux devtools extension. It is a great extension for debugging and look at changes in redux store.
I have tried that but still cannot fix my problem,this is the rest of my signinScreen,js
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
export const SignInScreen = (props, history) => {
const navigate = useNavigate
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'
const userlogin = useSelector((state) => state.userlogin)
const { userInfo, loading, error } = userlogin
const dispatch = useDispatch()
useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])
return (
<Container className="small-container">
<h1 className="my-3">Sign In</h1>
<Form onSubmit={submitHandler}>
<Form.Group className="mb-3" controlId="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
required
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
required
onChange={(e) => setPassword(e.target.value)}
/>
</Form.Group>
<div className="mb-3">
<Button type="submit">Sign In</Button>
</div>
<div className="mb-3">
New Customer?{' '}
<Link to={`/signup?redirect=${redirect}`}>Create new account</Link>
</div>
</Form>
</Container>
)
}
it still show the same error like before,i have no idea to solve this

I have successfully implemented the redux-persist with next-redux-wrapper in next js

Im getting data from the external api and storing it in the reducer.And im using redux-persist to persist the state while navigating from one page to another.But i have made left the whiteList as an empty array but all the state are being persisted?Need help
import "../assets/css/style.scss";
import "owl.carousel/dist/assets/owl.carousel.css";
import "owl.carousel/dist/assets/owl.theme.default.css";
import Layout from "../component/Layout/Layout";
import { wrapper } from "../redux/store";
import { useEffect } from "react";
import { useStore } from "react-redux";
function MyApp({ Component, pageProps }) {
const store = useStore((store) => store);
useEffect(() => {
{
typeof document !== undefined
? require("bootstrap/dist/js/bootstrap.bundle")
: null;
}
}, []);
return (
<Layout>
<Component {...pageProps} />;
</Layout>
);
}
export default wrapper.withRedux(MyApp);
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import rootReducer from "./index";
import { createWrapper, HYDRATE } from "next-redux-wrapper";
const middleware = [thunk];
let initialState={}
// BINDING MIDDLEWARE
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== "production") {
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const makeStore = ({ isServer }) => {
if (isServer) {
//If it's on server side, create a store
return createStore(rootReducer,initialState, bindMiddleware(middleware));
} else {
//If it's on client side, create a store which will persis
const persistConfig = {
key: "root",
storage: storage,
whiteList: [],
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer,initialState, bindMiddleware(middleware));
store.__persisitor = persistStore(store); // This creates a persistor object & push that
persisted object to .__persistor, so that we can avail the persistability feature
return store;
}
};
// export an assembled wrapper
export const wrapper = createWrapper(makeStore);
If you keep the whitelist an empty array then nothing will be persisted.
You have to put inside that string array the redux reducers values you want to be persisted.
Example:
const persistConfig = {
key: 'root',
storage: storage,
whiteList: ['cart', 'form', 'user'],
};

Redux Thunk not dispatching

I have installed Redux Thunk on my application and it's been working fine so far, all of the previous actions I've created are pulling out data from APIs successfully, however the following action is not even dispatching actions to my reducer, any idea what am I missing?
// my action
export const fetchClub = id => {
debugger
return (dispatch) => {
if (id){
dispatch ({type: 'START_PULLING_NIGHTCLUB'});
let targetUrl = `http://localhost:3001/nightclub`
fetch(targetUrl)
.then(res => {
debugger
return res.json()
})
.then(nightclub => dispatch({type: 'CURRENT_NIGHTCLUB', nightclubs: nightclub.result}))
.catch(error => {
console.log(error)
})
}}}
//my reducer
import {combineReducers} from "redux"
const rootReducer = combineReducers({
nightclubs: nightClubsReducer,
user: userReducer
})
export default rootReducer
function nightClubsReducer(state = {}, action) {
debugger
switch (action.type){
case 'ADD_NIGHTCLUBS':
debugger
let nightclubs = action.nightclubs
// filering the results just to show nightclubs rather than hotels
nightclubs = nightclubs.filter( function (nightclub){
return !nightclub.types.includes("lodging")
})
return {...state.nightclubs, nightclubs}
case 'CURRENT_NIGHTCLUB':
debugger
let nightclub = action.nightclub
return {...state.nightclubs, nightclub}
default:
return state
}}
function userReducer(state = {user: {logged_in: false}}, action){
let current_user = {}
switch (action.type){
case 'ADD_USER_LOCATION':
let coords = action.location.coords
return {...state.user, coords}
case 'CREATE_USER':
current_user = action.user
state.logged_in = true
return {...state.user, current_user}
case 'ADD_LOGGED_IN_USER':
current_user = action.user
if(state.user){
state.user.logged_in = action.user.logged_in}
return {...state.user, current_user}
default:
return state
}
}
I should be hitting the debugger on the first line of my nightClubsReducer however nothing happens.
My Nightclub component is connected properly as far as I'm aware:
import React, { Component } from 'react';
import Maya from '../assets/Mayaclubbio.jpg'
import '../NightClubPage.css'
import { connect } from 'react-redux';
import { fetchClub } from '../actions/NightClubs';
class NightClub extends Component {
constructor(props) {
super(props);
this.id = props.match.params.id
}
componentDidMount() {
fetchClub(this.id)
}
render() {
debugger
return (
<React.Fragment>
//HTML code
</React.Fragment>
)
}
}
const mapStateToProps = (state) => {
debugger
return {
nightclub: state.nightclubs.nightclub,
user: state.user
}
}
export default connect(mapStateToProps, { fetchClub })(NightClub);
I have no clue what could be failing as I'm using the same logic for the rest of my actions and they are working just fine.
I think calling the action from props should fix your issue
componentDidMount() {
this.props.fetchClub(this.id);
}

mapDispatchToProps function is undefined

I am trying to get redux working in my react-native app. Basically, I have a signIn action defined in my authActions.js file:
const signInAction = () => {
return {
type: 'signIn',
};
};
export { signInAction };
Then I have an authReducer defined as this in authReducer.js:
const initialState = {
isAuthenticated: false,
}
const authReducer = (state = initialState, action) => {
switch(action.type) {
case "signIn":
return Object.assign({}, state, {
isAuthenticated: true,
})
default: return state;
}
};
export default authReducer;
I combine that reducer in my rootReducer.js file
import { combineReducers } from 'redux';
import auth from 'app/src/redux/reducers/authReducer.js';
const rootReducer = combineReducers({
auth,
});
export default rootReducer;
and then created a store in reduxIndex.js:
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from 'app/src/redux/reducers/rootReducer.js';
let store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
export default store;
I wrapped my app in a <Provider> component, and that seems to be working fine (I can read from the state and see the value of isAuthenticated. However, when I try to dispatch an action using mapDispatchToProps in one of my views the function is undefined:
// More imports
// ...
import { connect } from 'react-redux';
import { signInAction } from 'app/src/redux/actions/authActions.js';
const mapStateToProps = (state) => {
return {};
}
const mapDispatchToProps = (dispatch) => {
return {
onSignIn: () => { dispatch(signInAction) },
};
}
class SignIn extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
}
}
onSignInPress() {
// ******* this is where the error occurrs ****
this.props.onSignIn();
}
render() {
const {navigation} = this.props;
return (
<View style={SignInStyles.container}>
<ScrollView>
<View>
<Button
large
title="SIGN IN"
backgroundColor={colors.primary}
onPress={this.onSignInPress}
/>
</View>
</ScrollView>
</View>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(SignIn);
I cant really see where I am going wrong, but im sure its a simple mistake somewhere. The specific error I get is :
"undefined is not an object. Evaluating this.props.onSignIn"
The onSignInPress callback isn't bound to any particular object, so when it gets called this is undefined.
The easy way to fix it is to use arrow syntax to make it always be bound. In your class definition:
onSignInPress = () => {
this.props.onSignIn();
}
Google found me this Medium article from Miron Machnicki which explains the differences and possible alternative syntaxes in pretty good detail.

how to reset redux store when log out?

I am trying to reset my storage when I log out but it doesn't seem to work at all.
As you can see I am using AsyncStorage for my store and I try to follow the answer from this post.
Here is my index.js from store folder
import thunk from 'redux-thunk';
import { createStore, compose, applyMiddleware } from 'redux';
import { AsyncStorage } from 'react-native';
import { persistStore, autoRehydrate } from 'redux-persist';
import rootReducer from '../reducers';
var defaultState = {};
export function configureStore(initialState = defaultState) {
var store = createStore(rootReducer, initialState, compose(
applyMiddleware(thunk),
autoRehydrate(),
));
persistStore(store, { storage: AsyncStorage });
return store;
}
and here is my index.js from reducers folder
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import { AsyncStorage } from 'react-native';
import authReducer from './authReducer';
import alertsReducer from './alertsReducer';
import jobsReducer from './jobsReducer';
import userDataReducer from './userDataReducer';
const appReducer = combineReducers({
form: formReducer,
auth: authReducer,
alerts: alertsReducer,
jobs: jobsReducer,
userData: userDataReducer
})
const rootReducer = ( state, action ) => {
if(action.type === 'UNAUTH_USER') {
Object.keys(state).forEach(key => {
AsyncStorage.removeItem(`persist:${key}`);
console.log(state)
});
}
return appReducer(state, action)
}
export default rootReducer
On Initial load of our application the reducer state is fresh.
We Can copy this initial default state and use it to assign to our reducer again on logging out, the way we can achieve this could be as follows.
Step 1: call an action on the application load that will copy reducer's initial state as the defaultState
Step 2: While logging out of the application we can simply reAssign the default state and it should work as new.
App root Component
componentDidMount() {
dispatch(ON_APP_LOAD)
}
App Reducer
const appReducer = combineReducers({
user: userStatusReducer,
analysis: analysisReducer,
incentives: incentivesReducer
});
let defaultState = null;
export default (state, action) => {
switch (action.type) {
case **ON_APP_LOAD**:
// will be assigned or called only once
defaultState = defaultState || state;
break;
case **RESET_STATE**:
// detaching the reference on reset
state = _.deepClone(defaultState);
return state;
default:
break;
}
return appReducer(state, action);
};
On Logout calling the action for resetting state
function* logoutUser(action) {
// on logout success
dispatch("RESET_STATE")
}
I assume you have the js file where all reducers are combined in one and thus you have:
const allReducers = combineReducers({
reducer: nameOfReducer
});
const rootReducer = (state, action) => {
switch (action.type) {
case CLEAR_ALL_REDUCERS_DATA:
return state = undefined;
default:
return allReducers(state, action)
}
};
export default rootReducer;
and in index file where you create a store you need to define
const store = createStore(rootReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
and then simply call it on logout:
dispatch(clearAllStoreData());
where clearAllStoreData is an action defined in your action file:
export const clearAllStoreData = () => {
return {
type: CLEAR_ALL_REDUCERS_DATA
}
};

Resources