Redux-saga store config error with combineReducer - redux

I try to config my saga store with Redux-saga, but I keep getting error about
Store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers.
I did google around check the documentation, adjust my code still have the issue, so hoping someone can point out for me. Thanks!
if I change this code
const sagaMiddleware = ReduxSaga.default();
to
const sagaMiddleware =()=> ReduxSaga.default();
I got other error about: sagaMiddleware.run is not a function
Main.js
const { createStore, applyMiddleware } = require("redux");
const { createSelector } = require("reselect");
const ReduxSaga = require("redux-saga");
const createSagaMiddleware = require("redux-saga");
const reducer = require("./reducers/index");
const { workerSaga } = require("./sagas/sampleSaga.js");
const sagaMiddleware = ReduxSaga.default();
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(workerSaga);
Reducers/index.js
const { combineReducers } = require("redux");
const sample1 = require("./sample1");
const sample2 = require("./sample2");
const rootReducer = combineReducers({
sample1,
sample2
});
module.exports = rootReducer;
Reducers/sample1.js
const {
STARTS_LOADING_SAMPLE,
FETCH_SAMPLE,
COMPLETE_FETCHING_SAMPLE
} = require("../actions/index");
const sampleInitialState = {
fetching: false,
complete: false,
sample1: []
};
//switch statement....
module.exports = {
sample1: (allReducer, sampleInitialState)
};

The reducer is a pure function that takes the previous state and an action, and returns the next state. But you are exporting an object instead of a function in Reducers/sample1.js file. Try changing it to something like this:
...
module.exports = function(state = sampleInitialState, action) {
// switch statements
};
More on reducers: https://redux.js.org/basics/reducers

Related

Why is the Redux state not updating with the API data?

I've been following the process for making an API call and storing it in global state with Redux using this project that I got from a Medium article. So far everything seems to work alright, no errors, but when I go to retrieve the global state there is nothing there. It doesn't seem to have been updated by the action that makes the API call. The relevant bits of code are as follows:
in reducers.js:
const initialState = {
mods: [],
pagination: { pageSize: 15, numPages: 1 },
sortFilter: "mostPopular",
};
const globalState = (state = initialState, action) => {
switch (action.type) {
case SET_MOD_LIST:
return { ...state, mods: state.mods };
case SET_MOD_DETAILS:
return { ...state };
default:
return state;
}
};
const rootReducer = combineReducers({
globalState,
});
export default rootReducer;
in actions.js:
export const fetchModList = (pagination, sortFilter = "mostPopular") => {
const { pageSize = 15, numPages = 1 } = pagination ?? {};
return async (dispatch) => {
const response = await fetch(
`https://www.myapi.com/mods?page=${numPages}&pageSize=${pageSize}&sortBy=${sortFilter}`
);
const resData = await response.json();
dispatch({ type: SET_MOD_LIST, mods: resData });
};
};
in index.js (Next.js root page):
const mods = useSelector((state) => state);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchModList({pageSize:2}));
}, [dispatch]);
console.log({mods})
This is 100% a result of Redux ignorance, this is my first project using it which I'm doing for an interview. Any help would be much appreciated!
Looks like you're setting mods to its own value mods: state.mods. Did you mean to set a value from action.payload rather than state.mods?

redux "undefined" when trying to get the data

im trying to read some data from Redux store but I keep getting undefined
this is based on redux template created using:
create-react-app [appname] --template redux
I know there most be an issue in how I read the data
this is my store:
import { configureStore } from "#reduxjs/toolkit";
import calculatorReducer from "../features/Data_calculator_slice";
export const store = configureStore({
reducer: {
calculator: calculatorReducer,
},
});
my actions and initialState:
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
value: [{ name: "test", wight: 18, grade: 12, gpa: "A" }],
};
export const counterSlice = createSlice({
name: "data",
initialState,
reducers: {
incrementByAmount: (state, action) => {
state.value += action.payload;
const currentValue = selectCount();
console.log(currentValue);
},
},
});
export const { incrementByAmount } = counterSlice.actions;
export const selectCount = (state) => state.calculator.data;
/* export const selectCount = (state) => state.counter.value;
*/
export default counterSlice.reducer;
but here when I'm trying to fetch the data using the code below it returns undefined
const count = useSelector(selectCount);
console.log(count);
You cannot use the same selector in your reducers as you do in your component. One is scoped to the slice you're defining, the other is global. Also in your reducer you aren't passing the state to the selectCount selector so it can't return anything from the state.
in your reducer the selector for your value is (state) => state.value
in your component the selector for your value is (state) => state.calculator.value

Redux dispatch fires with correct payload, but state is not updated

I'm building a game using react-redux and TypeScript. I've got TS and React down, but redux... less so.
When the player clicks the start game button, the main menu is hidden via manipulation of redux state and the "gameState" is generated. "gameState" contains all the relevant information for the game world and entities therein, and is several thousand lines of serializable JSON. This part is definitely working, the problem comes when I try to dispatch to update it. I can see from the Redux browser extension that the payload being sent to the reducer function updateGameState is correct, but after the dispatch has been completed it's as if it never happened.
My question is simple: what am I doing wrong?
The code for the previously-mentioned dispatch is:
let nGS = gameStateGenerator.create(scene)
dispatch(updateGameState(nGS))
The layout of this part of the redux logic is as shown below. The four children of multiverse, universes, species, connections, and players, should all be populated, but are not.
I'm using combined reducers, as follows. I've not used ES6 notation for the reducer object properties as part of my attempts to rule out causes (which hopefully speaks to my level of desperation).
store.ts (top level)
import { configureStore, ThunkAction, Action } from '#reduxjs/toolkit';
import gameState from '../features/gameState/gameState';
export const store = configureStore({
reducer: {
gameState: gameState
},
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>;
gameState.ts (first and so far only child of root)
import { combineReducers, createSlice, PayloadAction } from '#reduxjs/toolkit';
import { GameState } from '../../interfaces/GameState';
import flags, { initialState as flagsInitialState} from '../flags/flags';
import multiverse, { initialState as multiverseInitialState } from '../multiverse/multiverse';
export const initialState: GameState = {
multiverse: multiverseInitialState,
flags: flagsInitialState
};
export const gameState = createSlice({
name: 'gameState',
initialState,
reducers: {
updateGameState: (state: GameState, action: PayloadAction<GameState>) => {
return Object.assign({}, state, action.payload)
}
}
});
export const { updateGameState } = gameState.actions;
export default combineReducers({
flags: flags,
multiverse: multiverse
})
flags.ts
import { createSlice } from '#reduxjs/toolkit';
import { FlagsState } from '../../interfaces/Flags';
export const initialState: FlagsState = {
ui: {
showMainMenu: true,
showWelcome: true,
gameStarted: false,
gameLoading: false,
gameLoaded: false
}
};
export const flags = createSlice({
name: 'flags',
initialState,
reducers: {
startGame: (state: Required<FlagsState>) => {
return Object.assign({}, state, {
ui: {
...state.ui,
showMainMenu: false,
gameStarted: true,
gameLoading: true
}
})
},
gameLoaded: (state: Required<FlagsState>) => {
return Object.assign({}, state, {
ui: {
...state.ui,
gameLoaded: true
}
})
}
}
});
export const { startGame, gameLoaded } = flags.actions;
export const getState = (state: FlagsState) => state;
export default flags.reducer;
And finally multiverse.ts
import { createSlice, PayloadAction } from '#reduxjs/toolkit';
import { Multiverse } from '../../interfaces/Multiverse';
import { Universe } from './../../interfaces/Universes';
export const initialState: Multiverse = {
universes: [],
species: [],
connections: [],
players: []
};
export const multiverse = createSlice({
name: 'multiverse',
initialState,
reducers: {
setUniverses: (state: Required<Multiverse>, action: PayloadAction<Universe[]>) => {
return Object.assign({}, state, { universes: action.payload })
}
}
});
export const { setUniverses } = multiverse.actions;
export const getState = (state: Multiverse) => state;
export default multiverse.reducer;
I'm think the trouble comes from your reducer, you use Object assign and return.
Redux-Toolkit uses Immer to change the state with no mutating like this :
(state, action) => state.value = action.payload
See the doc, https://redux-toolkit.js.org/usage/immer-reducers#immutable-updates-with-immer
So for you, you can do something like :
state.univers = {...state.univers, ... action.payload}
with no return.

redux dispatch does not work

I am a redux beginner, everything is confusing for me right now. I am trying to create a store and dispatch a new value to the store, but it does not work, can someone help? thanks
import {createStore, combineReducers} from 'redux';
import uuid from 'uuid';
const state={
id:0,
numbers:[]
}
const randomNumber=()=>{...}
//reducer
const reducer=(state={},action)=>{
switch(action.type){
case 'ADD_NUMBER':
const newNumber={id:state.id+1, number:randomNumber()}
return {
...state,
id:uuid(),
numbers:[...state.numbers,newNumber]
}
}
}
//store
const store=createStore(reducer)
store.subscribe(()=>{const state=store.getState})
console.log('new state should update, but it does not: ', state)
const addNumber =()=>({type: 'ADD_NUMBER'})
//dispatch
store.dispatch(addNumber())
this is my error
Issue is in this line,
const reducer = (state = {}, action) => {
Note that you have initialized your state to an empty object {}. So when you try to spread the state.numbers you are getting this error because numbers is not defined in the initial state.
return {
...state,
id:uuid(),
numbers:[...state.numbers,newNumber]
}
A minimal example that reproduces your issue,
const bad = (state = {}) => {
return {
numbers:[...state.numbers]
}
}
bad();
To fix this you'll need to initialize your state with the default values,
const INITIAL_STATE = {
id:0,
numbers:[]
}
//reducer
const reducer = (state = INITIAL_STATE, action) => {
// Rest of the code
const INITIAL_STATE = {
id:0,
numbers:[]
}
const good = (state = INITIAL_STATE) => {
return {
numbers:[...state.numbers]
}
}
console.log(good());

Reselect Cannot read property 'get' of undefined

I am using reselect and react redux. I am trying to make a selector for a basic modal implementation.
my selector is
const selectModal = (state) => state.get('modal');
which throws the error of
Cannot read property 'get' of undefined
edit: It has been requested I show how I call select modal, though it should make no difference.
const mapStateToProps = createStructuredSelector({
isVisible: selectModalIsVisible(),
});
const mapDispatchToProps = {
hideModal,
showModal
};
export default connect(mapStateToProps, mapDispatchToProps)(Modal);
I believe this means the modal state container is not being found
Perhaps I am setting up my reducer or store incorrectly. My reducer is
function modalReducer(state = initialState, action) {
switch (action.type) {
case HIDE_MODAL:
return state.set(
'isVisible', false);
case SHOW_MODAL:
return state.set(
'isVisible', true);
default:
return state;
}
}
which is combined with combine reducers into a glob
export default function createReducer(asyncReducers){
return combineReducers({
route: routeReducer,
auth: authReducer,
modal: modalReducer,
...asyncReducers
});
}
and then injected into my store
function configureStore(initialState = {}, history) {
const middlewares = [
sagaMiddleware,
routerMiddleware(history),
];
const enhancers = [
applyMiddleware(...middlewares),
]
const store = createStore(
createReducer(),
fromJS(initialState),
compose(...enhancers)
);
store.runSaga = sagaMiddleware.run;
//store.close = () => store.dispatch(END)
store.runSaga(sagas);
store.asyncReducers = {};
return store;
}
var initialState = {}
const store = configureStore(fromJS(initialState), browserHistory);
The error within reselect is at lines 73/74 params = dependencies.map
var selector = function selector(state, props) {
for (var _len4 = arguments.length, args = Array(_len4 > 2 ? _len4 - 2 : 0), _key4 = 2; _key4 < _len4; _key4++) {
args[_key4 - 2] = arguments[_key4];
}
var params = dependencies.map(function (dependency) {
return dependency.apply(undefined, [state, props].concat(args));
});
return memoizedResultFunc.apply(undefined, _toConsumableArray(params));
};
So what am I doing wrong, do I need to do something with immutableJS differently to access the modal, or is my setup for the app incorrect? Thank you in advance for your feedback.
If you're using selectModal like you're using selectModalIsVisible, then your syntax is wrong. I'm pretty sure createStructuredSelector does not understand () => (state) => state.get('modal'). It would only accept (state) => state.get('modal')
Typically, my usages of createStructuredSelector will look like either
const getThing = (state, props) => state.things[props.thingId];
const getModal = state => state.get('modal');
const mapStateToProps = createStructuredSelector({
thing: getThing, // notice no parens
modal: getModal, // notice no parens
})
OR if I need selector factories:
// just pretend this selector was more complicated and needed memoization
const makeGetThing = () => createSelector(
state => state.things,
(state, props) => props.thingId,
(things, thingId) => things[thingId]);
const getModal = state => state.get('modal');
const makeMapStateToProps = () => createStructuredSelector({
thing: makeGetThing(), // yes parens
modal: getModal, // no parens
})

Resources