I am trying to do an API call through reducer.My code is working fine here but the problem is all the actions, reducers are inside same file. So I tried to separate the reducer and actions in different file, but it is not working. I have debugged by putting some console.log but it is not helping me. Can anyone tell me how to fix it? I am providing my code snippet and sandbox below.
https://codesandbox.io/s/redux-async-actions-os6nu
import {
SELECT_CHANNEL,
REQUEST_POSTS,
RECEIVE_POSTS,
DISPLAY_ALERT
} from "../actions";
//const reducer = (state = {}, action) => {
const fetchReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_DATA_START:
return {
...state,
fetching: true,
fetchingMessage: "fetch data start"
};
case RECEIVED_DATA:
return {
...state,
fetching: false,
fetched: true,
data: action.payload,
fetchingMessage: "received data"
};
case FETCH_DATA_ERROR:
return {
...state,
fetching: false,
error: action.payload,
fetchingMessage: "fetch data error"
};
default:
return state;
}
};
export default fetchReducer;
You need to initialize your state in the main reducer file as well as use the same action name across all your application. I have also used mapStateToProps and mapDispatchToProps methods to make it easy and simple to use redux with react (https://redux.js.org/basics/usage-with-react).
Reducer file
import { combineReducers } from "redux";
import { REQUEST_POSTS, RECEIVE_POSTS } from "../actions";
const initialState = {
fetching: false,
fetched: false,
data: [],
error: null,
fetchingMessage: ""
};
const fetchReducer = (state = initialState, action) => {
switch (action.type) {
case REQUEST_POSTS:
return {
...state,
fetching: true,
fetchingMessage: "fetch data start"
};
case RECEIVE_POSTS:
return {
...state,
fetching: false,
fetched: true,
data: action.payload,
fetchingMessage: "received data"
};
case "FETCH_DATA_ERROR":
return {
...state,
fetching: false,
error: action.payload,
fetchingMessage: "fetch data error"
};
default:
return state;
}
};
export default combineReducers({ posts: fetchReducer });
App.js
const mapStateToProps = state => {
return {
data: state.posts
};
};
const mapDispatchToProps = dispatch => {
return {
onFetch: () => {
dispatch(requestPosts);
},
onFetchSuccess: data => {
dispatch(receivedPosts(data));
}
// onFetchError: () => {
// // dispatch()
// }
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
Here is the working demo link forked from your CodeSandbox (https://codesandbox.io/s/redux-async-actions-436qu). Let me know if you do not understand anything.
Related
Hello i've been struggling with this for a while
i want to start with a initial state like so:
const initialState = {
cart: { cartItems: {}}
};
but i already have a couple reducers that make my initial state like so:
{
productList?:undefined,
productDetails?:undefined
}
but i get the error:
Type '{cart:{cartItems:{}}' has no properties in common with type '{productList?: undefined, productDetails? :undefined}'
my reducer looks like:
export const productListReducer = (state = { products: [] as any[] }, action:productListReducer) => {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return {
loading: true,
products: [] as any[],
};
case PRODUCT_LIST_SUCCESS:
return {
loading: false,
products: action.payload,
};
case PRODUCT_LIST_FAIL:
return {
loading: false,
error: action.payload,
};
default:
return state;
}
};
const reducer = combineReducers({
productList: productListReducer,
});
const store = createStore(
reducer,
initialState,
composeEnhancers(applyMiddleware(thunk))
);
any help is appreciated, thanks
My user structure is:
user = {
email: 'email',
flashcards: []
}
And i would like to add data into user's flashcards array (using redux)
My user-reducer
import { UserActionTypes } from './user.types';
const INITIAL_STATE = {
currentUser: null,
};
// GETS STATES OBJ AND RECIVES AN ACTION
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case UserActionTypes.SET_CURRENT_USER:
return {
...state,
currentUser: action.payload,
};
case UserActionTypes.ADD_FLASHCARD:
return {
...state,
currentUser: action.payload,
};
default:
return state;
}
};
export default userReducer;
user-actions
export const addFlashCard = user => ({
type: UserActionTypes.ADD_FLASHCARD,
payload: user.flashcards,
});
And when i'm doing so my payload is undefined.
Could you give me some hints?
You are currently overwriting currentUser with the value of user.flashcards from the redux action. To add new flashcards, the ADD_FLASHCARD branch of your reducer should look more like this:
case UserActionTypes.ADD_FLASHCARD:
return {
...state,
currentUser: {
...state.currentUser,
flashcards: [
...state.currentUser.flashcards,
...action.payload
]
}
};
I've been working with redux for the last couple weeks and was incorporating it into my projects when I ran into this wall. Pretty common reducer for modals being rendered so i can animate them before unmounting them.
const initialState = {
isModalOpen: false,
test: false
}
export default function(state = initialState, action) {
switch (action.type) {
case "modalInteraction":
return {
isModalOpen: action.payload
};
case "testModalInteraction":
return {
test: action.payload
};
default:
return state;
};
}
Sadly, the test property is still returning as undefined despite the fact that the other initial state in the same reducer can be called without a problem. I even removed all the testModalInteraction dispatches in the case that that somehow upset the datatype. I just can't spot the difference that keeps returning undefined.
When you return the new state, make sure to spread the initial state (...state) and then change whatever values you need to change.
const initialState = {
isModalOpen: false,
test: false
}
export default function(state = initialState, action) {
switch (action.type) {
case "modalInteraction":
return {
...state,
isModalOpen: action.payload
};
case "testModalInteraction":
return {
...state,
test: action.payload
};
default:
return state;
};
}
If it is still undefined, make sure the payloads are defined for both actions.
For example, your modalInteraction action could look like
export const modalInteraction = (bool) => ({
type: "modalInteraction",
payload: bool
})
P.S., you can destructure the action object. This allows you to use "type" instead of "action.type" and "payload" instead of "action.payload".
const initialState = {
isModalOpen: false,
test: false
}
export default function(state = initialState, action) {
const {type, payload} = action;
switch (type) {
case "modalInteraction":
return {
...state,
isModalOpen: payload
};
case "testModalInteraction":
return {
...state,
test: payload
};
default:
return state;
};
}
When I try to call any other action in redux it is setting one part of the state to it's initialState.
My root reducer looks like this
const rootReducer = combineReducers({
credentials: combineReducers({
cred,
user,
partner,
merchant,
bank,
error,
auth,
}),
preCredentials,
theme,
});
the part of the state that is being cleared is theme.
action dispatched
state diff
Why this actions that have anything to do with theme reducer can change its state.
Theme reducer
function theme(state = { ...INITIAL_THEME }, action) {
switch (action.type) {
case LOADING_THEME:
return {
...state,
isLoading: true,
};
case SAVE_THEME:
return {
...action.theme,
error: {
status: null,
message: '',
},
isLoading: false,
};
case CLEAR_THEME:
return INITIAL_THEME;
default:
return INITIAL_THEME;
}
}
reducer of dispatched action
function preCredentials(state = { ...INITIAL_STATE }, action) {
switch (action.type) {
case SAVE_USERNAME:
return { ...state,
user: { ...state.user,
fullName: action.fullName,
},
};
default:
return state;
}
}
function theme(state = { ...INITIAL_THEME }, action) {
switch (action.type) {
case LOADING_THEME:
return {
...state,
isLoading: true,
};
case SAVE_THEME:
return {
...state,
...action.theme,
error: {
status: null,
message: '',
},
isLoading: false,
};
case CLEAR_THEME:
return INITIAL_THEME;
default:
return state;
}
}
return state instead of initial state
I am building an isomorphic app and I have a strange problem with my store and reducers. When I import my reducers in the client side store, it all works:
import reducers from '../Reducers';
...
let store = createStore(reducers, initial, composeEnhancers(applyMiddleware(...middleware)));
export default store;
But when I try to do the same on the server:
import reducers from '../source/js/Reducers';
I am getting error in the console
Error: Expected the reducer to be a function.
My reducers are like this:
import { INSERT_POST, INSERT_CONTENT, BUMP_PAGE, FETCH_COLLECTION } from '../Actions/Content';
const defaultState = {
currentPage: 0,
nextPage: 1,
content: [],
fetched: []
};
const reducer = (state = defaultState, action) => {
switch (action.type) {
case INSERT_POST:
return { ...state, content: [ ...state.content, action.payload ], fetched: [ ...state.fetched, action.url ] };
case INSERT_CONTENT:
const newState = {...state};
newState.content[action.payload.id].content = action.payload.data;
return newState;
case `${FETCH_COLLECTION}_SUCCESS`:
return { ...state, fetched: [ ...state.fetched, action.meta.previousAction.payload.request.url ]};
case BUMP_PAGE:
return { ...state, currentPage: state.nextPage, nextPage: ++state.nextPage };
default:
return state;
}
};
export default reducer;
...
import { START_TRANSITION, END_TRANSITION, TOGGLE_TRANSITION } from '../Actions/Transitions';
const defaultState = {
loaded: true
};
const reducer = (state = defaultState, action) => {
switch (action.type) {
case START_TRANSITION:
return { ...state, loaded: false };
case END_TRANSITION:
return { ...state, loaded: true };
case TOGGLE_TRANSITION:
return { ...state, loaded: !state.loaded };
default:
return state;
}
};
export default reducer;
And the main reducer:
import { combineReducers } from 'redux';
import Transitions from './Transitions'
import Content from './Content';
export default combineReducers({
Transitions,
Content
});
I have no idea of what to do with this. It's the first time I ever see such error. What can I do?