Redux call 2 dispatch returns undefined - javascript

this is simple redux application for show posts and todos, when i call dispatch action SHOW_POSTS works nice, but SHOW_TODOS return undefined, why ?
let initialState = {
todos : ['buy milk', 'write code'],
posts : ['weekly news']
}
function counter(state = initialState, action) {
switch (action.type) {
case 'SHOW_POSTS':
return state.posts
break;
case 'SHOW_TODOS':
return state.todos
break;
case 'ADD_TODO':
return {
todos: [...state, action.payload]
}
break;
default:
return state
}
}
let store = createStore(counter)
store.subscribe(() => console.log(store.getState()))
console.log('show posts:')
store.dispatch({ type: 'SHOW_POSTS' })
console.log('show todos:')
store.dispatch({ type: 'SHOW_TODOS' })

You're supposed to return the entire state on your reducers as the return statement will replace the next value of state. What you're essentially doing here is replacing your initialState with just the posts. You can see this by reverting those dispatch calls. You'll see that posts will be empty.

Related

Store.Dispatch() Resetting Redux Store

I dispatch(action()) to trigger an action from outside my react component. It is working correctly in that it is triggering my action and updating the new item in my store. The problem is that it seems to be completely resetting everything else in my store, which to me at least makes it more of a problem than its worth.
Worth noting: I am using next.js.
Here is a basic idea of my flow:
I have a utils folder with a service where I am dispatching this action from:
import store from './store';
store.dispatch(myAction());
I have my actions
export const myAction = () => ({
type: HELP_ME,
payload: true,
});
const initialState = {
reducerVar: fase,
otherExistingVar: {},
otherExistingVarTwo: null,
otherExistingThree:null,
otherExistingVarFour: false,
};
const myReducer = (state = initialState, action) => {
switch (action.type) {
case HELP_ME: {
const reducerVar = action.payload;
}
default: {
return state;
}
}
};
export default myReducer;
I am not sure if I am misusing store.dispatch() because I dont see why anyone would use this technique if it completely wipes out the existing data in the store. Or is there a better way to trigger this simple action from outside my component.
Basically I want to dispatch this action without completely wiping out my store just like I would dispatch the action if I were in a component.
Thank You!
you should return the state with value in reducer like this.
const myReducer = (state = initialState, action) => {
switch (action.type) {
case HELP_ME: {
return {...state, reducerVar : action.payload}
}
default: {
return state;
}
}
};
What you are trying to do is fine. But your reducer must return the whole state with your change done by the action like below.
const myReducer = (state = initialState, action) => {
switch (action.type) {
case HELP_ME:
const reducerVar = action.payload;
return {...state, reducerVar }
default:
return state;
}
};

Stateful react-native functional component don't re-render when redux connected state change

I've started using hooks recently with react-native, so i have this connected component with a button that changes depending on a redux state. The button changes when redux state is updated inside the component, but not if it is from outside.
here is my button:
<TouchableOpacity
style={styles.goPremiumContainer}
onPress={() => pay()}>
{props.premiumLoading ? (
<ActivityIndicator size="small" color="#fff" />)
:
(<Text style={styles.goPremium}>
{i18n.t('premium:go_premium')}
</Text>)}
</TouchableOpacity>
//
//
//
const mapStateToProps = state => {
return {
premiumLoading: state.premiumLoading,
};
};
export default withNamespaces([], {wait: true})(
connect(mapStateToProps)(Premium),
);
also the reducer:
const initialState = false;
function updateButtonLoading(state = initialState, action) {
let nextState;
switch (action.type) {
case 'LOADING':
nextState = true;
return nextState || state;
case 'NOT_LOADING':
nextState = false;
return nextState || state;
default:
return state;
}
}
export default updateButtonLoading;
To update the button i call this action function:
async updatePremiumButton(actionType) {
console.log('actionType',actionType)
const action = {type: actionType};
const unsubscribe = store.subscribe(() => true);
await store.dispatch(action);
await unsubscribe();
},
THANKS!
The issue is the logic inside your reducer.
switch (action.type) {
case 'LOADING':
nextState = true;
return nextState || state;
case 'NOT_LOADING':
nextState = false;
return nextState || state;
default:
return state;
}
It appears that what you really want to do is return the value you assign nextState, but the logic reads differently.
Currently the logic reads: If nextState is truthy use it, else return previous state.
In the NOT_LOADING case, you are intending to return the value false, but the logic reads: If false is true, return false, else return true. So you can see why this would never work.
Instead, simplify the cases and just return what you want the state to be. The || conditions don't seem to be necessary.
function updateButtonLoading(state = initialState, action) {
switch (action.type) {
case 'LOADING':
return true;
case 'NOT_LOADING':
return false;
default:
return state;
}
}

how to dispatch an action to return default state

In a reducer function we usually return default state if it doesn't go to switch block.
function reducer(state = initialState, action: any): any {
switch (action.type) {
case SOME_TYPE:
return Object.assign({}, state, {
someBoolean: false
});
}
return state;
}
I tried dispatch(undefined, {type: undefined}) but I got error action may not have undefined type.
Is there a way that we could dispatch action which will return defalut state.
PS: Just trying a different way. Of-course we can have some action type and return default state.
If I have understood you correctly, you want to reset your state to the initial value.
switch (action.type) {
case SOME_TYPE:
return Object.assign({}, state, {
someBoolean: false
});
case 'DEFAULT':
return initialState;
default:
return state;
}
Then simply dispatch it:
dispatch({ type: 'DEFAULT' });
Note: remember to add a default case inside your reducer.
Not sure if I'm getting this correctly, but to return your state simply do:
function reducer(state = initialState, action: any): any {
switch (action.type) {
case SOME_TYPE:
return state;
}
return state;
}
Or to reset your initial state, do:
function reducer(state = initialState, action: any): any {
switch (action.type) {
case SOME_TYPE:
return initialState;
}
return state;
}
You should use the action's paylaod to update the state, and you can check if you have any payload, use it, otherwise go back to initial state
function reducer(state = initialState, action: any): any {
switch (action.type) {
case SOME_TYPE:
return Object.assign({}, state, {
someBoolean: action.payload.someBoolean || initialState.someBoolean
});
}
return state;
}
And dispatch an undefined :
dispatch({
type: SOME_TYPE,
payload: {
someBoolean: undefined
}
});
But a better way would be to have a type to revert back to initialState
function reducer(state = initialState, action: any): any {
switch (action.type) {
case SOME_TYPE:
return Object.assign({}, state, {
someBoolean: false
});
case REVERT:
return initialState;
}
return state;
}

loading status change after fetch data completely

I have action creator to get data from API and have another action creator for loading status and want change loading status when data completely fetched.
Now, I wrote following codes but not working good, Loading status changes to false before data fetched completely.
My ActionCreator:
export const loadingStatus = (bool) => {
return {
type: Constants.LOADING_STATUS,
isLoading: bool
};
}
const allFlashCards = (action) => {
return{
type: Constants.ALL_CARD,
...action
}
};
export const fetchAllFlashCards = () => {
return (dispatch) => {
dispatch(loadingStatus(true));
return axios.post(API.DISPLAY_ALL_CARDS)
.then((data)=>{
console.warn(data);
dispatch(allFlashCards(data));
dispatch(loadingStatus(false));
}).catch((error)=>{
console.warn(error)
});
}
};
and my Reducer:
const FlashCard = (state = [], action) => {
switch (action.type) {
case Constants.ADD_CARD:
return {...state, data: action.data};
break;
case Constants.ALL_CARD:
return {...state, data: action};
break;
default:
return state;
}
};
export const Loading = (status= false, action) => {
switch (action.type) {
case Constants.LOADING_STATUS:
return action.isLoading;
break;
default:
return status;
}
}
and in my component:
componentDidMount() {
this.props.fetchCards();
}
render() {
return(
<div>
{this.props.loading ?
<Loading/> :
Object.keys(this.props.cards.data).map(this.renderCard)
}
</div>
);
}
const mapStateToProps = (state) => ({
cards: state.main,
loading: state.loading
});
const mapDispatchToProps = (dispatch) => ({
fetchCards: bindActionCreators(fetchAllFlashCards, dispatch)
});
and combineReducer is:
import { combineReducers } from 'redux';
import FlashCard , { Loading } from './FlashCard.js';
import { routerReducer } from "react-router-redux";
export default combineReducers({
main: FlashCard,
loading: Loading,
routing: routerReducer
});
In my page, I have an error in console and it's:
Uncaught TypeError: Cannot read property 'data' of undefined and if put my codes in timeout fixed my bug :/
What should i do?
Your default state is wrong here:
const FlashCard = (state = [], action) => {
switch (action.type) {
case Constants.ADD_CARD:
return {...state, data: action.data};
break;
case Constants.ALL_CARD:
return {...state, data: action};
break;
default:
return state;
}
};
It should be an empty object {} instead of an empty array [], since you're returning objects.
This code
export const fetchAllFlashCards = () => {
return (dispatch) => {
dispatch(loadingStatus(true));
return axios.post(API.DISPLAY_ALL_CARDS)
.then((data)=>{
console.warn(data);
dispatch(allFlashCards(data));
dispatch(loadingStatus(false));
}).catch((error)=>{
console.warn(error)
});
}
};
Looks completely fine. loadingStatus(false) should not be called before setting the flash cards. Your reducers and action creators are synchronous (as they should). So, nothing of note there.
I saw that you're using action.data on the Constants.ADD_CARD action case, but in your code you do not dispatch any actions with that type. Do you do it somewhere else? Maybe that's where the error is?
EDIT:
Another place that you're using .data is in your renderer: this.props.cards.data. What's the value of the state.main?
How are you creating your rootReducer? It should be something like this:
const rootReducer = combineReducers({
main: FlashCard,
loading: Loading,
});
Are you using main there? Or maybe cards?
Finally, I fixed my problem:
In my actionCreator change fetchAllFlashCards method to following:
export const fetchAllFlashCards = () => {
return (dispatch) => {
dispatch(loadingStatus(true));
return axios.post(API.DISPLAY_ALL_CARDS)
.then(({data})=>{
dispatch(allFlashCards(data));
dispatch(loadingStatus(false));
}).catch((error)=>{
console.warn(error)
});
}
};
and in reducer change FlashCard reducer to following:
const FlashCard = (state = [], action) => {
switch (action.type) {
case Constants.ADD_CARD:
return {...state, data: action.data};
break;
case Constants.ALL_CARD:
return {...state, data: action.data};
break;
default:
return state;
}
};

Redux store double nesting

Im getting double nested state after api call. Here is it.
Screenshot from redux devtools
Here are my actions
export const getUserSuccess = user => ({
type: GET_USER_SUCCESS,
user,
});
export const getUserFailure = error => ({
type: GET_USER_FAILURE,
error,
});
export const getUserRequest = () => (
dispatch => (
axios.get('./user.json')
.then(response => dispatch(getUserSuccess(response.data)))
.catch(error => dispatch(getUserFailure(error)))
)
);
here is my user reducer
export default function user(state = {}, action) {
switch (action.type) {
case GET_USER_SUCCESS:
return {
...state,
user: action.user,
};
case GET_USER_FAILURE:
return {
...state,
error: action.error,
};
default:
return state;
}
}
and here is my root reducer
export default combineReducers({
user,
});
You need to spread the data which comes for user, no need to assign it to user. Your reducer is in it already
case GET_USER_SUCCESS:
return {
...state,
...action.user
};
If this reducer should update the state with the complete representation of the new user (and discard old data) you should just return the new user as state.
Can you give the following a shot?
export default function user(state = {}, action) {
switch (action.type) {
case GET_USER_SUCCESS:
return action.user;
case GET_USER_FAILURE:
return {
...state,
error: action.error,
};
default:
return state;
}
}
Judging by this quote
If I'll try that with array of users then array will be destroyed
Why not use Object.assign()?
return Object.assign({}, state, {user: [...state.user, ...action.user]})
Assuming that having multiple users is a possibility, why not just maintain the user property in your state as an array?

Categories

Resources