I have a problem with my isomorphic app created with React, Redux and Immutable.js. My reducer initial state is immutable Map, but when it's server side rendered and passed to client as preloadedState json then it's deserialised and it's type of object. Finally in my reducer on client side i have object instead of immutable Map, immutable methods are not working and i get errors.
Question is, how to pass preloadedState to createStore method in a way that in my reducer i would get immutable map instead of object? or i'm missing something?
collection.js
import Immutable from 'immutable';
const initialState = Immutable.Map({
isFetching: false
});
export default function collection(state = initialState, action) {
switch (action.type) {
case 'REQUEST_COLLECTION':
// here i have errors on client side that update method is not defined
return state.update('isFetching', true);
default:
return state;
}
}
reducer.js
import combineReducers from 'redux/lib/combineReducers';
import collection from './reducers/collection';
export default combineReducers({
collection,
});
index.js
import thunk from 'redux-thunk';
import createStore from 'redux/lib/createStore';
import applyMiddleware from 'redux/lib/applyMiddleware';
import compose from 'redux/lib/compose';
import reducer from './reducer';
const preloadedState = window.__PRELOADED_STATE__ || {};
const store = createStore(
reducer,
preloadedState,
compose(
applyMiddleware(thunk)
)
);
...
Related
I'm working with a React / Redux app that for some reason is getting a 'import/no-anonymous-default-export' warning for the app reducers even though the reducer functions are named.
userRedirect.js
import {SET_USER_REDIRECT} from "../actions/actionTypes";
const initialState = {
active: false,
title: '',
messages: [],
btnText: ''
};
const userRedirect = (state = initialState, action) => {
switch (action.type) {
case SET_USER_REDIRECT:
let newState = {...state};
newState = {...newState, ...action.payload.redirectData};
return newState;
default:
return state;
}
};
export default userRedirect;
This reducer is being imported into a index.js that is located in a reducers folder where userRedirect.js is also located. It then uses combineReducers method from redux to handle the reducers.
import {combineReducers} from 'redux';
import userData from './userData';
import userRedirect from './userRedirect';
export default combineReducers({
userData,
userRedirect
});
This is then imported into the index of a store folder where this happens:
import {createStore, applyMiddleware} from "redux";
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer from "./../reducers";
export default createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
To me it seems like there is a error with the plugin or something, but I've tried npm install, I've tried creating the userRedirect reducer using a function declaration and not a expression, but no matter what I do it keeps showing the warning and for the life of me I cannot figure out why.
Do you guys have any ideas or suggestions in regards to this? Am I just missing something?
Much appreciate it!
[
export default combineReducers({
userData,
userRedirect
});
export default createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
These two are anonymous export.
Change it to
const rootReducer = combineReducers({
userData,
userRedirect
});
export default rootReducer;
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
export default store;
Forgive me if does not look good ... answering from mobile.
Not able to format from mobile
I am trying to use enhanced object literal syntax in my redux reducer, and some of them are working while others return empty objects. Is there a reason why 'form', and 'auth' work fine in my example, but 'files' does not? When I change 'files' to the traditional syntax, it works just fine. When I execute the example below, it just returns an empty object
import { combineReducers } from 'redux';
import auth from './auth_reducer';
import userReducer from './user_reducer';
import propertyReducer from './property_reducer';
import files from './file_upload_reducer';
import { reducer as form } from 'redux-form';
const appReducer = combineReducers({
/* your app’s top-level reducers */
form,
auth,
user: userReducer,
properties: propertyReducer,
files
})
const rootReducer = (state, action) => {
if (action.type === 'UNAUTH_USER') {
state = undefined
}
return appReducer(state, action)
}
export default rootReducer;
js like this
import {createStore, applyMiddleware, combineReducers} from 'redux'
import {composeWithDevTools} from 'redux-devtools-extension'
import withRedux from 'next-redux-wrapper'
import nextReduxSaga from 'next-redux-saga'
import createSagaMiddleware from 'redux-saga'
import {initialState} from './initialState'
import globalSaga from '../sagas/global'
const sagaMiddleware = createSagaMiddleware()
export function configureStore (reducers,initialState) {
console.log(initialState)
let states = {}
Object.keys(reducers).map((key) => {
states[key] = initialState[key]
})
console.log(reducers)
console.log(states)
const store = createStore(
combineReducers({reducers}),
states,
composeWithDevTools(applyMiddleware(sagaMiddleware))
)
store.sagaTask = sagaMiddleware.run(globalSaga)
return store
}
export function withReduxSaga (BaseComponent, reducers) {
return withRedux(configureStore(reducers,initialState))(nextReduxSaga(BaseComponent))
}
export default function configureWithReduxSaga(component,reducers){
return withReduxSaga(component,reducers)
}
and the error is
Store does not have a valid reducer. Make sure the argument passed to
makeStore is not a function
What might be wrong, basically I would like to write function that adds reducers and store.global with specific sagas into nextjs component without much code, best would be something like this:
configureWithReduxSaga(
{reducer1, reducer2},
{saga1, saga2}
)
is it possible? I can create root reducer and root saga and pass it to every component, that is not hard to code, but is it performant?
combineReducers({reducers}),
Most likely that you have dictionary of reducers, but provide them wrapped into redundant object. Use rest spread operator combineReducers({...reducers}) or simple object passing combineReducers(reducers) in that case. See original documentation for it: https://redux.js.org/docs/api/combineReducers.html#arguments
If reducers is array, you should iterate over it and give own name to each reducer.
Not sure why I'm getting the following errors.
I'm just setting up my store, actions and reducers, I haven't called dispatch on anything yet.
Expected
App runs fine, Redux state is not updated
Results
src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import reducer from './reducer'
import App from './App'
import css from './coinhover.scss'
const element = document.getElementById('coinhover');
const store = createStore(reducer, compose(
applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
));
ReactDOM.render(
<Provider store={ store }>
<App />
</Provider>, element);
src/reducer/index.js
import { combineReducers } from 'redux'
import { coins } from './coins'
export default combineReducers({
coins
});
src/reducer/actions/coins.js
import * as api from '../../services/api'
import { storage, addToPortfolio } from '../../services/coinFactory'
export const ADD_COIN = 'ADD_COIN'
export function getCoin(coin) {
return dispatch => {
api.getCoin(coin)
.then((res_coin) => addToPortfolio(res_coin))
.then((portfolio) => dispatch(updatePortfolio(portfolio)));
}
}
export function updatePortfolio(portfolio) {
return {
type: ADD_COIN,
portfolio
}
}
finally src/reducer/coins/index.js
import { ADD_COIN } from './actions'
const initialState = [];
export default (state = initialState, action) => {
switch(action.type) {
case ADD_COIN:
return action.portfolio;
default:
return state;
}
}
Your issue lies with how you're importing your coins reducer:
import { coins } from './coins'
The latter tries to obtain a named export returned from the file in ./coins.
You are not using any named exports only export default, therefore you just need to import the file as follows:
import coins from './coins';
Using the latter will result with the fact that coins will then contain the value of export default; which will be the coins reducer.
Even when all your imports are correctly imported, this can still happen for one other reason. Circular Dependency!
In my case, this happeded because of a circular dependency in a file. I had two circular dependecy in the project that I created accedentally. Example: rootReducer.ts -> authSlice.ts -> rootReducer.ts.
These dependencies are often not as easy to debug. I used this package to check for circular dependencies. Once the circular dependency was removed, all was well.
Ah just found it, I was importing my coins reducer incorrectly...
import { combineReducers } from 'redux'
import coins from './coins' // because I have coins/index.js
export default combineReducers({
coins
});
instead of
import { coins } from './coins'
This was my fix:
import { combineReducers } from 'redux'
import { coins } from './coins'
export default combineReducers({
coinsState: coins || (() => null) // By adding this I resolved it.
});
If nothing worked for you this might be a circular dependency. I had a project like that requiring store state in the slice. Instead of using the store there was a thunk available that can provide you with the state without importing the store itself. If you have this edge case you can get state from thunk.
thunkAPI.getState()
In my case, I was not adding a default property to my reducers. When I added it, it worked.
here is my code;
const counterReducer = (state = 0, action) => {
switch(action.type){
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
export default counterReducer;
and combinde file;
import counter from './counter';
import { combineReducers } from 'redux';
const allReducers = combineReducers({
counter: counter,
});
export default allReducers;
import { createStore, combineReducers } from 'redux'
const reducer = (state = {}, action) => state
const rootReducer = combineReducers(reducer)
let store = createStore(rootReducer)
export default store
A warning is triggered when the store is created:
Store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers.
This warning comes up again when I attempt to use any of the store methods.
The error is simple - you did not pass an object to combineReducers:
const rootReducer = combineReducers({ reducer })
This will fix the problem.