Reload render issue after change props using Redux? - reactjs

I use the redux in my apps. there is only one state is define. I can change the state and render the screen. but whenever i change state props I can't reload the screen.
Code:
action-types.js
export const SET_NOTIFICATION = "SET_NOTIFICATION";
action.js
import {
SET_NOTIFICATION,
} from "./action-types";
let initialState = {
notyIndex: 0,
};
export const setNotyIndex = (notyIndex) => ({type: SET_NOTIFICATION, notyIndex});
reducer.js
import {
SET_NOTIFICATION,
} from "./action-types";
let initialState = {
notyIndex: 0,
};
export default reducer = (state = initialState, action) => {
switch (action.type) {
case SET_NOTIFICATION:
return Object.assign({}, state, {notyIndex: action.notyIndex});
break;
default:
return initialState;
break;
}
};
I connect the redux as below. DashBoard.js
import { setNotyIndex } from "./action";
import {connect} from "react-redux"
********* LIFE CYCLE START ************
componentWillMount(){
console.log('Call update');
console.log('Index is',this.props.notyIndex);
}
shouldComponentUpdate=()=>{
return true
}
componentDidUpdate=(prevProps, prevState, snapshot)=>{
console.log('Call update');
console.log('Index is',this.props.notyIndex);
}
componentDidMount() {
console.log('Call update');
console.log('Index is',this.props.notyIndex);
}
********* LIFE CYCLE END ************
const mapDispatchToProps = (dispatch) => {
return {
setNotyIndex: (notyIndex) => dispatch(setNotyIndex(notyIndex)),
}
};
const mapStateToProps = (state) => {
if (state === undefined) {
return {};
}
return {
notyIndex: state.notyIndex,
}
};
connect(mapStateToProps, mapDispatchToProps)(DashBoard);
value is set like.
setNotyIndex(1);
- As above code the no one lifecycle method called after set the value.
Thanks.

First when you use redux's Method you must call with
this.props.setNotyIndex(1);
and When you use redux's veriable in your component you must have you use
this.props.notyIndex
You can console in your mapStateToProps method to get changes like under
const mapStateToProps = (state) => {
console.log("State veriable : ", state)
if (state === undefined) {
return {};
}
return {
notyIndex: state.notyIndex,
}
};
When you change your redux veriable and if you use that veriable in your code then relative component rerender it selt. But if there some issue then you can call setState menually after redux method calling, like under
this.props.setNotyIndex(1);
this.setState({
});
I hope it work for you.......

Related

React-Redux mapStateToProps doesn't update component

So I simplified my code but basically I have a straight-forward redux store:
/* store.js */
import { createStore } from 'redux';
const reducer = (state = {}, action) => {
if (action.type === 'action') state.data = data;
return state;
}
const store = createStore(reducer);
store.subscribe(() => {
console.log(store.getState()); // returns the right state, updates properly
});
export default store;
A Loader that that pulls the data from the server and dispatches it to the store:
/* Loader.js */
class Loader {
dispatch (allDocuments) {
store.dispatch({
type: 'action',
data: data
});
}
async fetchData () {
try {
const allDocuments = await ajaxCall('GET', '/fetchData');
this.dispatch(allDocuments);
return allDocuments;
} catch (e) {
console.error(e);
}
}
}
export default Loader;
And then this is my App.js file where I fire the Loader fetch method every 5 seconds and map the store state to a React component:
/* App.js */
import Loader from './Loader';
const loader = new Loader();
setInterval(async () => {
await loader.fetchData();
}, 5000);
const App = ({
data
}) => {
console.log(data); //doesn't update
return (
<div>
<p>{data}</p>
</div>
)
};
const mapStateToProps = state => ({data: state.data,})
export default connect(mapStateToProps)(App);
So the problem here is that the component does not update. Loader dispatches properly, and the redux store does get updated but the data prop in App remains an empty object, and doesn't refire the render method.
Why is mapStateToProps not updating the component when the store state changes?
mapStateToProps expects that you will not mutate the state. The problem is your reducer, which is mutating the state variable by assigning directly to state.data.
To avoid mutating the state, you'll want to return a new copy of the object whenever you change the data. Like this:
const reducer = (state = {}, action) => {
if (action.type === 'action') {
return {
...state,
data: action.payload
}
return state;
}
Of course if you only have one type of action than redux is not the right tool for the job.
Your reducer doesn't save the action.data payload. It also isn't returning a new state object reference.
const reducer = (state = {}, action) => {
if (action.type === 'action') state.data = data; // <-- mutation
return state;
}
When the action type matches then you should return a new state object reference with the action.data payload.
const reducer = (state = {}, action) => {
if (action.type === 'action') {
return {
...state,
data: action.data;
};
}
return state;
}

Redux not triggering re-render even-though I am not mutating my state in my reducer

I'm having some difficulty with React Redux. It's related to components not re-rendering after a state change. Every question that is asked online refers to it probably being that you are mutating the state, however, I am almost 100% sure that I am not making that mistake. After having tried multiple approaches I just don't know what is going wrong.
Here is my original reducer code:
import * as actionTypes from '../actions/actionTypes';
import { updateObject } from '../utility';
const initialState = {
jwsToken: null,
accessToken: null,
};
const updateTokens = (state, action) => {
return updateObject(state, {jwsToken: action.jwsToken, accessToken: action.accessToken})
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.UPDATE_TOKENS: return updateTokens(state, action);
default:
return state;
};
};
export default reducer
I'm using a utility function (updateObject) to make a copy of my object that I want to return in the reducer. It looks like this:
export const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
};
};
I also tried it without the updateObject utility function and using Object.assign():
const updateTokens = (state, action) => {
return Object.assign({}, state, {
jwsToken: action.jwsToken,
accessToken: action.accessToken,
})
};
I fear that I'm doing something super stupid, but I've spent too much time on this now not to ask. Can someone please tell me what I'm doing wrong?
Edit
Here is my component:
import React, { Component } from "react";
import * as actions from "../../store/actions/index";
import { connect } from "react-redux";
class Calendar extends Component {
componentDidMount () {
if (this.props.accessToken) {
this.onGetEvents()
}
}
onGetEvents = () => {
this.props.getEventsSelectedMonth(this.props.selectedMonth,
this.props.accessToken)
}
render() {
return (
//JSX here
)
}
}
const mapStateToProps = state => {
return {
accessToken: state.accessToken,
selectedMonth: state.selectedMonth
};
};
const mapDispatchToProps = dispatch => {
return {
getEventsSelectedMonth: (selectedMonth, accessToken) =>
dispatch(actions.getEventsSelectedMonth(selectedMonth, accessToken))
};
};
export default connect(mapStateToProps, mapDispatchToProps) (Calendar);
To avoid the infinite loop you can make sure to call the required function only when the value is changed:
componentDidUpdate(prevProps, prevState) {
if (prevProps.accesstoken !== this.props.accessToken) {
this.onGetEvents()
}
}
The infinite loop happens as without the check we would continuously change the state on every update(which occurs because of the state change).
From your comments, I'm assuming you're getting different access tokens every time so maybe you just want to call onGetEvents when you get an accessToken for the first time.
componentDidUpdate(prevProps, prevState) {
// only call when the previous token is falsy and there's a new truthy token
if (!prevProps.accesstoken && this.props.accessToken) {
this.onGetEvents()
}
}

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

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