I call get_user_profile_api inside the app after login. On successful login my app has user and token in the state. But as soon as GET_USER_PROFILE_SUCCESS is called, the state is left only with userData and isLoading. user and token are removed from the state.
import { CONST } from '../../utils/Const';
import * as types from './types';
const initialState = {
isLoading: false,
user: null,
userData: null,
isLoggedIn: false,
token: '',
};
export default authReducer = (state = initialState, action) => {
console.log('action :: ', action);
switch (action.type) {
case types.GET_USER_PROFILE_LOADING:
case types.LOGIN_LOADING:
return {
isLoading: true,
};
case types.GET_USER_PROFILE_SUCCESS:
return {
...state,
isLoading: false,
userData: action.payload,
};
case types.LOGIN_SUCCESS:
return {
...state,
isLoading: false,
isLoggedIn: true,
user: action.payload.user,
token: action.payload.token,
};
case types.LOGIN_ERROR:
case types.GET_USER_PROFILE_ERROR:
return {
...state,
isLoading: false,
};
default:
return {
...state,
isLoading: false,
};
}
};
there is nothing wrong with this code I think the issue is where you are calling these actions from and you can call them inside a use-effect hook to get a user profile if there is a token or maybe you are not mapping state to props correctly
I was not writing ...state in the LOADING case. That's why my state was being removed.
Related
I am trying to create a webpage using React.
used redux for data access.
My problem is that when I click on any product, it shows me a perfect page, but if I go back and then again click on another product, my page shows me the data of the new product, but it does not change the photo height in CSS.
home page
product 1product 2(its doesnt changing first image height)
export const productDetailsReducer = (state = { product: {} }, action) => {
switch (action.type) {
case PRODUCT_DETAILS_REQUEST:
return {
loading: true,
...state <------i am sending the previous state
}
case PRODUCT_DETAILS_SUCCESS:
return {
loading: false,
product: action.payload,
}
case PRODUCT_DETAILS_FAIL:
return {
loading: false,
error: action.payload
}
case CLEAR_ERRORS:
return {
...state,
error: null
}
default:
return state
}
You must first iterate, and then provide the changes.
return {
...state <------ iterate
loading: true, <----- set new values
}
In addition, you should always iterate, just to make sure, you're not changing the whole state.
export const productDetailsReducer = (state = { product: {} }, action) => {
switch (action.type) {
case PRODUCT_DETAILS_REQUEST:
return {
...state
loading: true,
}
case PRODUCT_DETAILS_SUCCESS:
return {
...state,
loading: false,
product: action.payload,
}
case PRODUCT_DETAILS_FAIL:
return {
...state,
loading: false,
error: action.payload
}
case CLEAR_ERRORS:
return {
...state,
error: null
}
default:
return state
}
So I have a reducer which doesn't seem to be updating the state at all whenever I called the 'LOGIN' action. It could be a problem with my React Redux code. It's either my component is not getting re rendered whenever the store's state changes or the reducer is not changing the stores state at all.
Reducer ------
const initialState = {
messages: [],
loginDetails: {
email: '',
password: ''
},
activeUsers: [],
loginActive: false
}
const messageReducer = (state = initialState, action) => {
switch(action.type) {
case 'ADD_MESSAGE':
if(state.messages.length < 50) {
let newStateMessages = [...state.messages]
newStateMessages.unshift(action.payload);
console.log(newStateMessages);
return {...state, messages: newStateMessages};
} else {
let newStateMessages = [...state.messages]
newStateMessages.pop();
newStateMessages.unshift(action.payload);
return {...state, newStateMessages};
}
case 'LOGIN':
console.log('LOGIN');
console.log(action);
const newLoginDetails = {
email: action.payload.email,
password: action.payload.password
};
console.log({...state, loginDetails: newLoginDetails});
return {...state, loginDetails: newLoginDetails};
case 'UPDATE_USERS':
const newActiveUsers = action.payload;
return {...state, activeUsers: newActiveUsers};
case 'LOGIN_ACTIVE':
return {...state, loginActive: true};
case 'LOGIN_EXIT':
return {...state, loginActive: false};
default:
return state;
}
}
export const store = createStore(messageReducer);
React Redux connect -----
const mapStateToProps = state => {
return { ...state }
}
export default connect(mapStateToProps)(Home);
This mapStateToProps returns...
{
activeUsers: []
dispatch: ƒ dispatch(action)
loginActive: true
loginDetails: {email: "", password: ""}
messages: []
__proto__: Object
}
when it should return...
{
activeUsers: []
loginActive: true
loginDetails: {email: "example#gmail.com", password:
"password"}
messages: []
__proto__: Object
}
I have tested for sure that the dispatch to the reducer is getting called, and the payload is correct. However, the reducer is failing to update the state with the LOGIN action type.
Can you try this:
const mapStateToProps = ({activeUsers,loginActive,loginDetails,messages}) => ({
activeUsers,
loginActive,
loginDetails,
messages
})
I have the following Reducer:
const initialState = {}
const dishReducer = (state = initialState, action) => {
switch (action.type) {
case 'LOAD_DISHES':
return (action.dishes)
case 'LOAD_DISHES_ERROR':
console.log("load dishes error")
return state
case 'LOAD_DISHES_SUCCESS':
console.log("load dishes success")
return state
default:
return state;
}
};
export default dishReducer;
And the following action(s):
import {database} from '../../config/fbConfig'
export const startLoadingDishes = (dishes) => {
return (dispatch) =>{
return database.ref('products-dishes').once('value').then((snapshot) => {
let dishes = {}
snapshot.forEach((childSnapshot) => {
let parentkey = childSnapshot.key
let dishArray = [];
childSnapshot.forEach((dish) =>{
dishArray.push(dish.val())
});
dishes[childSnapshot.key] = dishArray;
})
dispatch(loadDishes(dishes))
}).then(() => {
dispatch({ type: 'LOAD_DISHES_SUCCESS' });
}).catch(err => {
dispatch({ type: 'LOAD_DISHES_ERROR' }, err);
});
}
}
export const loadDishes = (dishes) => {
return {
type: 'LOAD_DISHES',
dishes
}
}
The 'startLoadingDishes' action is called inside the componentDidLoad() of a certain Component. However, I want to alter the initial state of my dishReducer so that it includes additional information, as follows:
const initialState = {
value : {},
loaded: false,
loading: false,
error: false
}
So now 'action.dishes' returned by reducer [in 'LOAD_DISHES' case] should be put inside the 'value' part of the state, instead of it being the whole state. Also, the 'loaded' part of the state should be set to true if dishes have already been loaded earlier, and so on. I understand this is fairly simple but as I am new to React+Redux, I don't know how to alter the Action/Reducer codes properly (while keeping state immutability). Any help is appreciated.
I originally asked the question, here is how I solved it (not sure if this is the 'best' way though):
New reducer file:
const initialState = {
value : {},
loaded: false,
loading: false,
error: false
}
const dishReducer = (state = initialState, action) => {
switch (action.type) {
case 'LOAD_DISHES':
return {
value: action.dishes,
loading: !state.loading,
loaded: false, //this might need to be set to true
error: false
}
case 'LOAD_DISHES_ERROR':
console.log("load dishes error")
return {
...state, //or: state.value, as the other parts of state are being overwritten below
loaded: false,
loading: false,
error: true
}
case 'LOAD_DISHES_SUCCESS':
console.log("load dishes success")
return {
...state, //better: state.value
loaded: true,
loading: false,
error: false
}
default:
return state;
}
};
export default dishReducer;
No change in actions file.
Now, inside the 'Main' component, I was originally accessing the state as such:
class Main extends Component {
componentDidMount() {
this.props.startLoadingDishes();
}
render() {
return (
//some code
)
}
}
const mapStateToProps = (state) => {
return {
dishes: state.dishes //to access dishes: dishes.value
}
}
export default connect(mapStateToProps, actions)(Main)
The Main component code also stayed the same, with the difference that now I use 'dishes.value' instead of just 'dishes' to access the value of dishes from the state (and dishes.loaded for loaded, and so on). And now the action caller inside componentDidMount is as follows:
componentDidMount() {
if(!this.props.dishes.loaded){
this.props.startLoadingDishes();
console.log("loading dishes from database")
}
}
In logger seen anything is normal data loaded successfully.
You can see it by follow this link https://i.stack.imgur.com/uTLpr.jpg
But why this crypto props is undefined and how to fix it.
Does anyone have any idea?
Component
class CryptoContainer extends Component {
componentWillMount(){
this.props.Fetchcoin()
}
renderCoinCards() {
const { crypto } = this.props;
console.log('Crypto : '+crypto)
return crypto.data.map((coin,index) => //Add this
<CoinCard
key={coin.name}
coin_name={coin.name}
symbol={coin.symbol}
price_usd={coin.price_usd}
percent_change_24h={coin.percent_change_24h}
percent_change_7d={coin.percent_change_7d}
/>
)
}
}
function mapStatetoProps(state){
return{
crypto:state.crypto
}
}
export default connect(mapStatetoProps,{Fetchcoin})(CryptoContainer)
>
Action
export default function FetchCoin(){
return dispatch => {
dispatch({type:FETCHING_COIN_DATA})
return axios.get(`https://api.coinmarketcap.com//v1/ticker/?limit=10`)
.then(res => {
dispatch({type:FETCHING_COIN_DATA_SUCCESS,payload:res.data})
})
.catch(err => {
dispatch({type:FETCHING_COIN_DATA_FAIL,payload:err.data})
})
}
}
Reducer
const initialState = {
isFetching: null,
data: [],
hasError: false,
errorMessage: null
}
export default function (state = initialState, action) {
switch (action.type) {
case FETCHING_COIN_DATA:
return Object.assign({}, state, {
isFetching: true,
data: null,
hasError: false,
errorMessage: null
})
case FETCHING_COIN_DATA_SUCCESS:
return Object.assign({}, state, {
isFetching: false,
data: action.payload,
hasError: false,
errorMessage: null
})
case FETCHING_COIN_DATA_FAIL:
return Object.assign({}, state, {
isFetching: false,
data: action.payload,
hasError: true,
errorMessage: action.err
})
default:
return state
}
}
And store
const middleware = applyMiddleware(thunk,promise,logger)
const rootReducer = combineReducers({
crypto:CyptoReducer
})
const Store = createStore(
rootReducer,middleware
)
export default Store
I fixed misspelling in store but when I add crypto.data.map() in renderCoinCard
data seem still not working debugger shown
TypeError: Cannot read property 'map' of null
I think there is a problem with your declaration of rootReducer.Here you have mentioned "cypto" instead of "crypto". Fix this and it will work as here only redux declares a reducer and as you have declared here as "cypto" it expects "cypto" and fails.
Running redux sagas and react together and totally stumped why i'm still getting undefined errors. I have an initial state set so that .map can just run on that, i've tried null, undefined, empty arrays - always the same error.
Where am I going wrong, guys & gals?
App.js
{
isFetching ? (
<button disabled>Fetching...</button>
)
:
<div>
<button onClick={onRequest}>Click For API Names</button>
<ul>
{users.map((each, i) => <li key={i}>{each.name}</li>)}
</ul>
</div>
};
...
const mapStateToProps = state => {
return {
isFetching: state.isFetching,
users: state.users,
error: state.error,
};
};
const mapDispatchToProps = dispatch => {
return {
onRequest: () => dispatch({ type: constants.API_REQUEST }),
};
};
reducer.js
const initialState = {
isFetching: false,
users: [{ name: '' }, { name: '' }],
error: null,
};
export const namesReducer = (state = initialState, action) => {
switch(action.type) {
case constants.API_REQUEST:
return {
...state,
isFetching: true,
error: null,
};
case constants.API_SUCCESS:
return {
...state,
isFetching: false,
users: action.payload,
error: null,
};
case constants.API_FAILURE:
return {
...state,
isFetching: false,
users: [''],
error: action.error,
};
default:
return state;
};
}
users: action.payload
action payload probably isn't an array