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
Related
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;
}
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.
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()
}
}
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;
}
}
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.......