Redux store updating strangely - reactjs

I am having a code that looks like this
reducer
const initState = { isLoggedIn: false };
const isLoggedInReducer = (state = initState, action) => {
switch (action.type) {
case "LOG_IN":
return { isLoggedIn: true };
case "LOG_OUT":
return { isLoggedIn: false };
default:
return {isLoggedIn:false};
}
};
export default isLoggedInReducer;
action
export const logIn = () => {
return {
type:'LOG_IN'
}
}
export const logOut = () => {
return {
type:'LOG_OUT'
}
}
screen
import React from 'react'
import {useDispatch,useSelector} from 'react-redux'
import {logIn , logOut} from '../redux/actions/isLoggedInAction'
const AuthScreen = () => {
console.log('auth page re-rendered')
let status = useSelector(state => state.isLoggedIn)
console.log(status)
const dispatch = useDispatch()
return <>
<h1> auth is {status} status</h1>
<button onClick={()=>dispatch(logIn())}>authenticate me</button>
<button onClick={()=>dispatch(logOut())}>un auth me</button>
</>
}
export default AuthScreen
The problem is, something causes the app to render twice, and update the store
The variable should not have changed unless I dispatch an action, which I clearly did not. Also the value of the variable is logged out but doesnt print inside the h1 tag.
If I change the default case of the reducer to something like
const initState = { isLoggedIn: false };
const isLoggedInReducer = (state = initState, action) => {
switch (action.type) {
case "LOG_IN":
return { isLoggedIn: true };
case "LOG_OUT":
return { isLoggedIn: false };
default:
return {isLoggedIn:' hello world'};
}
};
export default isLoggedInReducer;
Then I get this output
The above output suggests that the default case was somehow run. But again, I did not dispatch any action to it. I am only reading the data using the "useSelect" but something is dispatching actions that I dont know about.
I am very new to redux and trying to learn. Thanks for your time.

In your default case, return the state as is:
default:
return state;
If you return a new object, React will treat the state as having changed and rerender the component, as equality is checked by ref by default.

Related

how to use mapDispatchToProps in react redux

I am new in redux.
My code :
Home Screen
<Text> {{this.props.mycity}} </Text>
const mapStateToProps = function(state) {
return {
mycity: state.layersFlag.baseDistrictADhabi //consist true/false
}
}
export default connect(mapStateToProps)(HomeScreen);
Sidemenu Screen :
UI
<Switch onValueChange={(flag) => {
this.props.toggleCity();
} value={this.state.city} />
const mapDispatchToProps = dispatch => {
return {
toggleCity: () => {
dispatch({ type: "changeCity" })
}
};
};
export default connect(null, mapDispatchToProps)(SideMenuScreen);
Store and reducer setup :
const initialState = {
city : {
mycity: true
}
};
const reducer = (state = initialState, action)=>{
switch (action.type) {
case "changeCity":
return Object.assign({}, state, {
mycity: action.payload.mycity
})
default:
return state;
}
}
const Store = createStore(reducer);
I am stuck in sidemenu. How to dispach in mapDispatchToProps method:
How to pass action in mapDispatchToProps in sidemenu?
If my assumptions on what your Switch component does is correct, it would trigger the onValueChange event-listener when you pass in this.state.city to the value prop. You end up calling this.props.toggleCity() to dispatch your changeCity action. I think the set-up is correct for here...
However, it looks like your reducer is expecting an action.payload which you never passed in as part of the action.
const reducer = (state = initialState, action)=>{
switch (action.type) {
case "changeCity":
return Object.assign({}, state, {
mycity: action.payload.mycity
})
default:
return state;
}
}
So yes the dispatch is working correctly, but you are not passing all the necessary data for your reducer to return a new piece of state.
You need to update your mapDispatchToProps, your event-handler and your reducer to something like
<Switch onValueChange={(flag) => {
this.props.toggleCity(this.state.city);
} value={this.state.city} />
const mapDispatchToProps = dispatch => {
return {
toggleCity: (myCity) => {
dispatch({ type: "changeCity", payload: myCity })
}
};
};
export default connect(null, mapDispatchToProps)(SideMenuScreen);
Your reducer also seems to have an extra key, you don't need to access the mycity prop in payload if its already the payload. Update to:
const reducer = (state = initialState, action)=>{
switch (action.type) {
case "changeCity":
return Object.assign({}, state, {
mycity: action.payload
})
default:
return state;
}
}
Adding on, if you want your Hone component to re-render with the new data in your redux-state, you can do something like this.
In your HomeScreen component, make use of a state-variable to save your abudhabi or whatever city-value and call componentDidUpdate() to setState and re-render your component.
class HomeScreen extends React.Component{
state = {
abudhabi: false
}
//when the component gets the new redux state this will trigger
componentDidUpdate(prevProps){
if(this.props.abudhabi !== prevProps.abudhabi){
this.setState({
abudhabi: this.props.abudhabi
})
}
}
}

React Native Redux AsyncStorage Returns Null

I implemented async reducer and i am trying to return asyncstorage value but when i call dispatcher it prints value in console but don't update initial state.
userReducer.js :
import {AsyncStorage} from "react-native";
const initialState = {
fullname: null
}
const userReducer = async (state = initialState, action) => {
switch (action.type) {
case 'GET_USERNAME':
const uname= await AsyncStorage.getItem('#uinfo:name', '')
console.log('uname: '+uname)
return { fullname: uname}
}
return state
}
export default userReducer;
In above console prints users name but dont't set the state.
I tried to Set dispatch in componentDidMount:
async componentDidMount() {
const uname= await this.props.getNAME()
console.log('uname: '+ this.props.fullname)
}
console.log('uname: '+ this.props.fullname) return undefined
My mapDispatchToProps:
function mapDispatchToProps(dispatch) {
return {
getNAME: () => dispatch({ type: 'GET_UNAME' })
}
}
Do i missing something, Why the state is not setting
fullname is an element from state as you assign initialState to state, so you can access fullname like this.
import {AsyncStorage} from "react-native";
const initialState = {
fullname: null
}
const userReducer = async (state = initialState, action) => {
switch (action.type) {
case 'GET_USERNAME':
const uname= await AsyncStorage.getItem('#uinfo:name', '')
console.log('uname: '+uname)
state.fullname = uname;
return { ...state }
}
return state
}
export default userReducer;
AsyncStorage.getItem() returns promise, so the statement return { fullname: uname} runs before the await statement and once return statement gets executed the function is exited. You can try this instead
AsyncStorage.getItem('#uinfo:name', '').then(res=>{
if(res)
return {
...state,
fullname: uname }
})
Maybe you are using it the wrong way,
actions does not return anything.
You need to map state to props using mapStateToProps config in connect.
const mapStateToProps = ({
userReducer
}) =>{
const {fullname} = userReducer;
return fullname;
}
give this to connect,
export default connect(
mapStateToProps,
mapDispatchToProps
)(YouClass);
you can access your data using, this.props.fullname.
cmponentDidUpdate and componentWillReceiveProps will be fired when reducer will return data.
Note: you have returned data from reducer like this
return { fullname: uname} // never do this, because you will lost all other data/variables other than fullname.
instead do this,
return { ...state,fullname: uname} //this will not do anything to other variables

Simple React components won't update after Redux state change

I have the following React component connected to the Redux store, and even though the state of the store changes (I checked), the component prop userIsLogged won't change its value. Any help is appreciated!
const mapDispatchToProps = (dispatch) => bindActionCreators({deauthenticateUser}, dispatch);
const mapStateToProps = (state) => ({ userIsLogged: state.auth.loggedUser !== null });
const Logout = (props) => {
const { userIsLogged } = props;
return (
userIsLogged?
<Button
variant="outlined"
color="primary"
onClick={(e) => {
props.deauthenticateUser();
history.push('/login');
}}>
Exit
</Button>
:<div />
);
}
Logout.propTypes = {
userIsLogged: PropTypes.bool.isRequired
};
export default connect(mapStateToProps, mapDispatchToProps)(Logout);
The reducer is as follow:
const initialState = {
jwt: null,
loggedUser: null,
isLoading: false
}
export default function auth(state = initialState, action) {
switch (action.type) {
case 'GOT_JWT':
return Object.assign(state, { jwt: action.jwt });
case 'USER_LOGGING_IN':
return Object.assign(initialState, { isLoading: action.isLoading });
case 'USER_LOGGED_IN':
return Object.assign(state, { loggedUser: action.loggedUser, isLoading: false });
case 'NO_JWT':
return initialState;
case 'USER_LOGGED_OUT':
return initialState;
default:
return state;
}
}
In your reducer code you're mutating the passed state object.
What happens next is that react treats the state as unchanged (it's the same object), hence it does not re-render it.
To fix it change the
Object.assign(state, { jwt: action.jwt });
to
Object.assign({}, state, { jwt: action.jwt });
It would create a new object and copy properties from the original state + the new ones.

Redux store updating, but React views is not

Hi when i console log my components props (passed down from redux) i get the initial state which is null. however using the react inspector i have the result of the axios request. I tried reading dozens of similar problems but cannot seen to resolve my issue.
Actions
import { searchService } from '../api/searchService';
export const actions = {
FETCH_USERS: 'FETCH_USERS',
}
export const searchUsers = () => dispatch => {
searchService.get('/search')
.then((result) => {
dispatch({
type: actions.FETCH_USERS,
payload: result
})
})
}
Reducers
import { actions } from '../actions';
export default (state = null, action) => {
switch(action.type) {
case actions.FETCH_USERS:
return action.payload;
default:
return state;
}
}
Search Component
function mapStateToProps ({search}) {
return {search};
}
const mapDispatchToProps = dispatch => ({
searchUsers: () => dispatch(searchUsers())
});
export default connect(mapStateToProps, mapDispatchToProps)(withAuth()(Search));
Your problem is in the Reducer
First you should make an initial state, and then you need to edit this state in order for redux to feel the changes and update
Check the code below and let me know if it worked for you.
import { actions } from '../actions';
const INITIAL_STATE= {search: ""};
export default (state = INITIAL_STATE, action) => {
switch(action.type) {
case actions.FETCH_USERS:
return {...state, search: action.payload};
default:
return state;
}
}

Not able to fetch state defined using immutable fromJS in selector for my react-redux app

this is my selector file for Login component
import { createSelector } from 'reselect';
const authentication = () => (state) => state.get('login');
const getCurrentAuthData = () => createSelector(
authentication,
(loginState) => loginState.get('currentUser')
);
export {
authentication,
getCurrentAuthData,
};
and this is the reducer file which describes the state for Login component
import { fromJS } from 'immutable';
import { loginConstants } from './constant';
let user = JSON.parse(localStorage.getItem('user'));
const initialState = user ? fromJS({ loggedIn: true, user }) : fromJS({});
function loginReducer(state = initialState, action) {
switch (action.type) {
case loginConstants.LOGIN_REQUEST:
return state
.set('loggingIn', true)
.set('user', action.true)
case loginConstants.LOGIN_SUCCESS:
return state
.set('loggedIn', true)
.set('user', action.true)
case loginConstants.LOGIN_FAILURE:
return fromJS({});
case loginConstants.LOGOUT_REQUEST:
return fromJS({});
case loginConstants.LOGOUT_SUCCESS:
return fromJS({});
case loginConstants.LOGOUT_FAILURE:
return fromJS({});
default:
return state
}
}
export default loginReducer;
Now the problem is it's giving error as loginState.get is not a function.
PS I am referencing the React-boilerplate [https://github.com/react-boilerplate/react-boilerplate] for code and basically copy-pasting code (learning phase).
The problem is solved, as expected there was in error in writing the statement.
I changed the statement from
const authentication = () => (state) => state.get('login');
//here it is returning a function to authentication
to
const authentication = (state) => state.get('login');
//here it is returning `state.get('login')` to authentication

Resources