Redux state is getting undefined while navigating between tabs - reactjs

I have three tabs in my application.I used to get values from each and save the data in the third tab.The application works fine if the navigation order is not changed.(i.e)Tab1-->Tab2-->Tab3.
But if when I navigate from Tab3-->Tab2-->Tab3 .The value from the Tab1 gets null.
similarly when I navigate from Tab3-->Tab1-->Tab3 .The value from the Tab2 gets null.
Reducer.js
const initialState = {
external: [],
internal: [],
usercode:'',
vehicleImage:'',
checkInoutcontrols:[]
}
const Reducer = (state = initialState, action) => {
switch (action.type) {
case 'insertExternalCoordinates':
return { external: action.value }
case 'insertInternalCoordinates':
return { internal: action.value }
case 'insertUserCode':
return {usercode:action.value}
case 'insertImage':
return {vehicleImage:action.value}
case 'insertCheckInOutControls':
return {checkInoutcontrols:action.value}
}
return state;
}
export default Reducer
Tab1
//Saving state ---redux
const mapStateToProps = state => ({
external: state.external
})
//inserting values using function --- redux
const mapDispatchToProps = dispatch => ({
insertExternalCoordinates: (value) => dispatch({ type:
'insertExternalCoordinates', value: value })
});
export default connect(mapStateToProps, mapDispatchToProps)
(CheckOutExternal)
Tab2
//Saving state ---redux
const mapStateToProps = state => ({
insertCheckInOutControls: state.insertCheckInOutControls
})
//inserting values using function --- redux
const mapDispatchToProps = dispatch => ({
insertCheckInOutControls: (value) => dispatch({ type:
'insertCheckInOutControls', value: value })
});
export default connect(mapStateToProps, mapDispatchToProps)
(CheckOutParts)
Tab3
//Saving state ---redux
const mapStateToProps = state => ({
insertCheckInOutControls: state.insertCheckInOutControls
external:state.external,
usercode: state.usercode,
checkInoutcontrols:state.checkInoutcontrols
})
//inserting values using function --- redux
const mapDispatchToProps = dispatch => ({
insertExternalCoordinates: (value) => dispatch({ type:
'insertExternalCoordinates', value: value }),
insertCheckInOutControls: (value) => dispatch({ type:
'insertCheckInOutControls', value: value })
});
export default connect(mapStateToProps, mapDispatchToProps)
(CheckOutSignature)
Apps.js -----store is created
import React, {Component} from 'react';
import {KeyboardAvoidingView} from 'react-native';
import AppNavigation from './main';
import Reducer from './modules/Reducers';
import {Provider} from 'react-redux'
import {createStore} from 'redux';
const store = createStore(Reducer)
const App = () => ({
render() {
return (
<Provider store={store}>
<AppNavigation/>
</Provider>
);
}
})
export default App;
Can anyone help me to solve this.

It seems issue is in the reducer, you are only returning the updated key-value pair instead of full reducer state. So after each update reducer will have only one key-value pair, the last updated one. Add ...state to each object you are returning, it will keep the other properties.
Write you reducer like this:
const Reducer = (state = initialState, action) => {
switch (action.type) {
case 'insertExternalCoordinates':
return { ...state, external: action.value }
case 'insertInternalCoordinates':
return { ...state,, internal: action.value }
case 'insertUserCode':
return { ...state,, usercode:action.value }
case 'insertImage':
return { ...state, vehicleImage:action.value }
case 'insertCheckInOutControls':
return { ...state, checkInoutcontrols:action.value }
}
return state;
}
Check this example for more details:
let obj = { a:1, b: 2 };
function update(key, value) {
switch(key) {
case 'a': return { ...obj, a: value }
case 'b': return { ...obj, b: value }
}
return obj;
}
let newObj = update('a', 10);
console.log('obj', obj);
console.log('newObj', newObj);

Related

REACT: How can I access another functiın return?

I am trying to access myCitiesArray that is in HandleCitiesArray func. I need to access redux using useSelector hook. How can I do that? I want to delete an object inside of initialState.myCities array
import ActionTypes from "../constants/ActionTypes";
import { useSelector } from "react-redux";
const initialState = {
myCities: [],
aCity: {},
getCityById: {},
filteredCities: [],
};
const HandleCitiesArray = () => {
const myCitiesArray = useSelector((state) => state.allWeathers.myCities);
return myCitiesArray;
};
export const WeatherReducer = (state = initialState, { type, payload }) => {
console.log(HandleCitiesArray().myCitiesArray); //try to print it here
switch (type) {
case ActionTypes.GETBY_CITYNAME:
return {
...state,
aCity: payload,
myCities: [...state.myCities, payload],
};
case ActionTypes.GETCITYBYID:
return {
...state,
getCityById: payload,
myCities: HandleCitiesArray().filter(
(item) => parseInt(item.id) !== parseInt(payload.id)
),
};
default:
return state;
}
};
Only Call Hooks from React Functions
You already have access to the state in the reducer function where you can grab myCities same as you did on this line myCities: [...state.myCities, payload], and log it out

Difference in the value received in this.props when using mapStateToProps (react-redux) vs store.getState()

I am facing an issue in my code base so I have made a sample code to demonstrate the issue.
link for the codesandbox code
App.js
import React, { Component } from 'react';
import './App.css';
import { connect } from 'react-redux';
import { handleDataInit, handlePageChange, handleDataAdded } from './appDataAction';
import First from './First';
import Second from './Second';
import { reduxStore } from "./store";
class App extends Component {
handleChange = (pageNumber, pageTitle) => {
let data = {
val1: "val1",
val2: "val2",
val3: "val3"
}
this.props.handleDataAdded(data);
console.log("app Data", this.props.appData);
console.log('app data in redux store ', reduxStore.getState().appData);
this.props.handlePageChange({ pageNumber, pageTitle });
}
render() {
return (
<div>
<button onClick={() => this.handleChange(1, "first_page")}>1</button>
<button onClick={() => this.handleChange(2, "second_page")}>2</button>
{
this.props.appData.pageNumber === 1 ?
<First />
:
<Second />
}
</div>
);
}
}
const mapStateToProps = (state) => {
console.log('map state to props state value is ', state);
return ({
appData: state && state.appData
})
}
const mapDispatchToProps = (dispatch) => {
return ({
handleDataInit: (data) => dispatch(handleDataInit(data)),
handlePageChange: (newPage) => dispatch(handlePageChange(newPage)),
handleDataAdded: (data) => dispatch(handleDataAdded(data))
})
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
screenshot for the two console.log
browser console log:
appDataAction.js
export const handleDataInit = (data) => {
return ({
type: "data_init",
payload: data
});
}
export const handlePageChange = (newPage) => {
return ({
type: "page_change",
payload: newPage
});
}
export const handleDataAdded = (data) => {
return ({
type: "data_added",
payload: data
});
}
appDataReducer.js
const initialState = {
pageNumber: 1,
pageTitle: "first_page",
}
export const appDataReducer = (state = initialState, action) => {
switch (action.type) {
case "data_init":
if (Object.keys(state).length > 2) {
return state
}
else {
let newState = Object.assign({}, state, action.payload);
// console.log("new state in init ", newState);
return newState;
}
case "page_change":
// console.log('action.payload', action.payload);
let newState2 = {
...state,
pageNumber: action.payload.pageNumber,
pageTitle: action.payload.pageTitle
}
// console.log('new state is ', newState2);
return newState2;
case "data_added":
let newState3 = Object.assign({}, state, action.payload);
// console.log("new state in data added ", newState3);
return newState3;
default:
return state;
}
}
From react-redux documentation
The first argument to a mapStateToProps function is the entire Redux store state (the same value returned by a call to store.getState()).
can somebody explain why there is difference in the two console's.
I have debugged and found out that after return from reducer mapStateToProps is called and it gets the updated value of state
then why is this.props.appData is not up to date in the handleChange function.
I believe it could be something related to dirty state but if it is proper for getState() in the function it should be for this.props.appData too.

How do I return an array in the reducer?

I am making an app where I need to return boards as an array to map through in the Home component
Application state is (should be) something like this:
{
boardsList: {
boards: An array of retrieved boards
boardCount: their length
...other stuff related to boards
},
The boardList reducer:
import {
HOME_PAGE_LOADED,
CHECK_IF_BOARD
} from '../constants/actiontypes'
const boards = (state = {}, action) => {
switch (action.type) {
case HOME_PAGE_LOADED:
return {
...state,
boards: action.payload.boards
}
default:
return state
}
}
export default boards;
The action payload:
payload:
boardCount: 2
boards: Array(2)
0: {_id: 1, name: "popps"}
1: {_id: 9, name: "sc"}
My Home Component that is retrieving boards:
import { Link } from 'react-router-dom'
import { HOME_PAGE_LOADED, CHECK_IF_BOARD } from '../constants/actiontypes'
import React, { Component } from 'react'
import agent from '../agent'
import { connect } from 'react-redux'
const mapStateToProps = state => ({
...state.boardList
})
const mapDispatchToProps = dispatch => ({
onLoad: (payload) => {
dispatch({ type: HOME_PAGE_LOADED, payload })
},
check: (payload) => {
dispatch({ type: CHECK_IF_BOARD, payload })
}
})
export default connect(mapStateToProps, mapDispatchToProps)(Home)
In my Home component, the type of this.props.boards is an object, however, the redux state shows it's an array!!
The problem is in your reducer because your state is nested. Change the reducer to:
switch (action.type) {
case HOME_PAGE_LOADED:
const boards = action.payload.boards || []
return Object.assign({}, state, {boardsList: {boards,
boardCount:boards.length}})
default:
return state
}
Greetings
Using spread operator:
switch (action.type) {
case HOME_PAGE_LOADED:
const boards = action.payload.boards || []
return ({...state, boardsList: {boards,
boardCount:boards.length}})
default:
return state
}

Dispatching action calls incorrect reducer

Summary
In order to learn Redux, I am incorporating some state, actions, reducers, and trying to see how they are used in React Components.
I have set up a test object...
const initialState = {
navigationCount : 0,
someNumber : 500,
someList : ['aa',22,'c5d6','45615'],
};
...and aim to:
increment the navigationCount by 1 when visiting pages
add or subtract from someNumber
push() & pop() elements from someList.
Versions
Currently using gatsby ^2.5.0, react ^16.8.6, and react-redux ^6.0.1.
Code
actions & reducers
import { combineReducers } from 'redux';
import {
PAGE_INCREMENT,
NUMBER_INCREASE,
NUMBER_DECREASE,
LIST_PUSH,
LIST_POP,
} from './actionTypes.js';
// state
const initialState = {
navigationCount : 0,
someNumber : 500,
someList : ['aa',22,'c5d6','45615'],
};
// action creators returning actions
export const pageIncrementer = navigationCount => {
return {
type: PAGE_INCREMENT,
navigationCount,
};
};
export const numberAdder = numberToAdd => {
return {
type: NUMBER_INCREASE,
numberToAdd,
};
};
export const numberMinuser = numberToMinus => {
return {
type: NUMBER_DECREASE,
numberToMinus,
};
};
export const listPusher = itemToAdd => {
return {
type: LIST_PUSH,
itemToAdd,
}
};
export const listPopper = () => {
return {
type: LIST_POP,
}
};
// reducers
const pageIncrementReducer = (state = initialState, action) => {
switch (action.type) {
case PAGE_INCREMENT:
return Object.assign({}, ...state, {
navigationCount: action.navigationCount+1
});
default:
return state.navigationCount;
}
};
const numberChanger = (state = initialState, action) => {
switch (action.type) {
case NUMBER_INCREASE:
return Object.assign({}, ...state, {
someNumber: state.someNumber+action.numberToAdd,
});
case NUMBER_DECREASE:
return Object.assign({}, ...state, {
someNumber: state.someNumber-action.numberToMinus,
});
default:
return state.someNumber;
};
};
const listChanger = (state = initialState, action) => {
switch (action.type) {
case LIST_POP:
return Object.assign({}, ...state, {
someList: state.someList.pop(),
});
case LIST_PUSH:
return Object.assign({}, ...state, {
someList: state.someList.push(action.itemToAdd),
});
default:
return state.someList;
}
}
// store
const rootReducer = combineReducers({
pageIncrementReducer,
numberChanger,
listChanger,
});
export default rootReducer;
React Component
import React from 'react';
import Layout from '../components/common/Layout.jsx';
import LandingBanner from '../components/landing/LandingBanner.jsx';
import LandingNavgrid from '../components/landing/LandingNavgrid.jsx';
import LandingApp from '../components/landing/LandingApp.jsx';
import { connect } from 'react-redux';
import {
PAGE_INCREMENT,
NUMBER_INCREASE,
NUMBER_DECREASE,
LIST_PUSH,
LIST_POP,
} from '../state/actionTypes';
class LandingPage extends React.Component {
constructor(props){
super(props);
this.state = {
appliedNum: 2000,
};
}
componentDidMount(){
// this.props.pageIncrement(); // => numberChanger returned undefined
// this.props.numberIncrease(4444); // => pageIncrementReducer returned undefined
// this.props.numberDecrease(4444); // => pageIncrementReducer returned undefined
// this.props.listPush(4444); // => pageIncrementReducer returned undefined
this.props.listPop();
}
render(){
return (
<Layout>
<LandingBanner/>
<LandingNavgrid/>
<LandingApp/>
</Layout>
)
}
}
const filterNumbers = (list=[]) => {
console.log('filterNumbers list: ', list);
return list.filter(listElement => !!Number(listElement));
};
const mapStateToProps = (state, ownProps) => {
return {
someNumber: state.someNumber,
someList: filterNumbers(state.someList),
navigationCount: state.navigationCount,
};
};
const mapDispatchToProps = (dispatch) => {
return {
pageIncrement: () => dispatch({ type: PAGE_INCREMENT }),
numberIncrease: () => dispatch({ type: NUMBER_INCREASE }),
numberDecrease: () => dispatch({ type: NUMBER_DECREASE }),
listPush: () => dispatch({ type: LIST_PUSH }),
listPop: () => dispatch({ type: LIST_POP }),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps,
)(LandingPage);
Errors
redux.js:449 Uncaught Error: Given action "LIST_POP", reducer
"pageIncrementReducer" returned undefined. To ignore an action, you
must explicitly return the previous state. If you want this reducer to
hold no value, you can return null instead of undefined.
first of all, you always need to return state on the default switch case.
default:
return state;

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;
}
}

Resources