I'm wondering about others approach to Redux useSelector hook. For example in my code:
const selectedStage = useSelector(state => state.basket.stage);
IntelliJ/WebStorm are giving me warning Unresolved variable basket
I see two options: turning off this warning or use destructuring like:
const { basket: { stage: selectedStage} } = useSelector(state => state);
I think it is much less readable. Is there anything I miss or could be done better?
------- EDIT ---------
If I simplify my Redux setup to basic like:
const basketReducer = (state = initialState.basket, action) => {
switch (action.type) {
case CHANGE_BASKET_SETTINGS:
return { ...state, ...action.payload };
...
export default combineReducers({
basket: basketReducer,
...
import allReducers from "../reducers";
export const store = createStore(allReducers);
I get same warning. It's really annoying.
Related
I am starting a new next js project,we are migrating from a normal react app to a next app.
we intend to use redux toolkit for our global state management and we are using server side rendering.so we came across next-redux-wrapper npm package which looked like it solves most of our problems with redux and ssr but form some reason when ever we use server side rendering on one of the pages the HYDRATE action from next-redux-wrapper is getting called atleast twice sometimes even 4 times.What exactly is going wrong because the article i referred to seems to work fine,i have attached my Redux store details and the Redux slice details,and my getServerSideProps function details.
import { createStore, applyMiddleware, combineReducers } from "redux";
import count from "../ReduxSlices/CounterSlice";
import { createWrapper, HYDRATE } from "next-redux-wrapper";
import { configureStore } from "#reduxjs/toolkit";
const combinedReducer = combineReducers({
count,
});
const reducer = (state, action) => {
if (action.type === HYDRATE) {
const nextState = {
...state, // use previous state
...action.payload, // apply delta from hydration
count: {
count: state.count.count + action.payload.count.count,
},
};
return nextState;
} else {
return combinedReducer(state, action);
}
};
export const makeStore = () =>
configureStore({
reducer: reducer,
});
export const wrapper = createWrapper(makeStore, { debug: true });
and my Redux slice is
import { createSlice } from "#reduxjs/toolkit";
import { HYDRATE } from "next-redux-wrapper";
const initialState = {
count: 0,
};
const counterSlice = createSlice({
name: "counter",
initialState: initialState,
reducers: {
increment: (state) => {
state.count = state.count + 1;
},
},
});
export const { increment } = counterSlice.actions;
export default counterSlice.reducer;
and finally this is how i dispatch an action inside getServerSideProps
export const getServerSideProps = wrapper.getServerSideProps(
(store) => async () => {
store.dispatch(increment());
console.log("server", new Date());
}
);
The console log is logging only once but the HYDRATE action is getting dispatched atleast two times...any insight will be helpful,thank you.
I had the same issue. Disabling react strict mode worked for me.
const nextConfig = {
// reactStrictMode: true,
...
}
module.exports = nextConfig
I am new to react and redux ... so please forgive me for noobie mistakes. I read several documentations of redux and came to conclusion that this is how i should store state of react component. I require redux because there are many more nested components which need quick access of data. however... when I try to export store ... I can't find how to do so.
My App.js
export default class App extends Component {
state = {
xx: null,
yy: null
}
componentDidMount(){
//some logic
// State gets data from api
this.setState({
xx: someval,
yy: someval2
});
}
render() {
const obj = {
xx: this.state.xx,
yy: this.state.yy
};
userReducer(obj,updateUserDetails());
const store = createStore(userReducer);
return (
<Provider store={store} >
<UserDetails props ={this.state} />
</Provider>
);
}
}
// Reducer function
export const userReducer = (state, action) => {
console.log("in reducer " + JSON.stringify(state));
switch(action.type) {
case 'UPDATE_USER_INFO':
state = {
...state,
xx: action.payload.xx,
yy: action.payload.yy
}
break;
}
return state;
}
// Doesn't work
export const store = createStore(userReducer)
// Action
export const updateUserDetails = () => {
return {
type: 'UPDATE_USER_INFO'
}
}
I can't figure out way to export store so that it is accessible to nested components. Kindly help
Thanks in advance!
From looking on your code, I can see a few issues that each one can be your problem.
while reducer first loads, it has to hold initial value for the state to begin with, not related to the one you want to be set at component mount
// assiging empty obj as initial value
export const userReducer = (state = {}, action)
Actions of redux are higher order functions, returning object to be dispatch
export const updateUserDetails = () => (dispatch, getState) => {
return dispatch ({
type: 'UPDATE_USER_INFO'
})
}
About your createStore, declare here as well initial value
// assign empty obj as initial value
export const store = createStore(userReducer, {})
hope it is helpful, anyhow I recommended on looking through the docs again
As part of my ongoing project to learn React (I'm natively an ASP.NET guy) I've hit this issue. I have a suite of React apps in which I want to use some common UI elements, so I've attempted to break these out into a separate npm package. For the shared components themselves this has worked fine.
However, some of these components depend on redux actions to operate, so I've tried to bundle these actions and a reducer function into the external package. Here's a simplified version of my actions\index.js:
export const SNACKBAR_MESSAGE = "SNACKBAR_MESSAGE";
export const SNACKBAR_HIDE = "SNACKBAR_HIDE";
export function showSnackBarMessage(message) {
console.log('hit 1');
return (dispatch, getState) => {
console.log('hit 2');
dispatch(hideSnackBar());
dispatch({
type: SNACKBAR_MESSAGE,
message: message
});
}
}
export const hideSnackBar = () => {
type: SNACKBAR_HIDE
};
And this is reducer\index.js:
import {
SNACKBAR_MESSAGE,
SNACKBAR_HIDE
} from "../actions";
const initialState = {
snackBarMessage: null,
snackBarVisible: false
};
export default function UiReducer(state = initialState, action) {
switch(action.type) {
case SNACKBAR_MESSAGE:
return Object.assign({}, state, {
snackBarMessage: action.message,
snackBarVisible: true
});
case SNACKBAR_HIDE:
return Object.assign({}, state, {
snackBarMessages: '',
snackBarVisible: false
});
default:
return state;
}
}
This is the same code that worked fine when part of the original project. These are exported by my package's entry point file like this:
// Reducer
export { default as uiReducer } from './reducer';
// Actions
export { showSnackBarMessage as uiShowPrompt } from './actions';
export { hideSnackBar as uiHidePrompt } from './actions';
Then in my consuming project, my default reducer looks like this:
import { routerReducer } from 'react-router-redux';
import { combineReducers } from 'redux';
import { uiReducer } from 'my-custom-ui-package';
// Import local reducers
const reducer = combineReducers(
{
// Some local reducers
ui: uiReducer
}
);
export default reducer;
The problem is when I try to dispatch one of these actions imported from my external package. I include the action, e.g. import { uiShowPrompt } from "my-custom-ui-package"; and dispatch it like dispatch(uiShowPrompt("Show me snackbar")); then I see the two console messages (hit 1 and hit 2) displayed, but then the following error:
Uncaught TypeError: Cannot read property 'type' of undefined
at store.js:12
at dispatch (applyMiddleware.js:35)
at my-custom-ui-package.js:1
at index.js:8
at middleware.js:22
at store.js:15
at dispatch (applyMiddleware.js:35)
at auth.js:28
at index.js:8
at middleware.js:22
The store itself looks like this:
import { createStore, combineReducers, applyMiddleware, compose } from "redux";
import thunk from 'redux-thunk';
import { browserHistory } from "react-router";
import {
syncHistoryWithStore,
routerReducer,
routerMiddleware
} from "react-router-redux";
import reducer from "./reducer";
const loggerMiddleware = store => next => action => {
console.log("Action type:", action.type);
console.log("Action payload:", action.payload);
console.log("State before:", store.getState());
next(action);
console.log("State after:", store.getState());
};
const initialState = {};
const createStoreWithMiddleware = compose(
applyMiddleware(
loggerMiddleware,
routerMiddleware(browserHistory),
thunk)
)(createStore);
const store = createStoreWithMiddleware(reducer, initialState);
export default store;
I'm afraid I don't understand this error. I don't see what I'm doing differently other than essentially moving identical code from my local project to an npm package. Since neither the actions nor reducer actually depend on redux, my npm package doesn't itself have a dependency on react-redux. Is that a problem? If there's anything else I could share to help you help me just let me know. Like I say, I'm still fairly new to all this so clearly there's something I'm not getting right!
The problem might be in declaration of hideSnackBar function
export const hideSnackBar = () => {
type: SNACKBAR_HIDE
};
Here the function is trying to return an Object Literal from Arrow Function. This will always return undefined. As the parser doesn't interpret the two braces as an object literal, but as a block statement. Thus the error, Cannot read property 'type' of undefined as store is expecting an action with property type.
Replace code like this and see if it works.
export const hideSnackBar = () => ({
type: SNACKBAR_HIDE
});
The parentheses forces it to parse as Object Literal. Hope this helps
I had exported it like
export default userReducer();
and not like this:
export default userReducer;
Just get rid of that ()
Found out that it was case of wrong order in receiving the arguments when using redux-thunk.
// wrong argument order
const anAction = () => (getState, dispatch) => {...}
// correct one
const anAction = () => (dispatch, getState) => {...}
I'm trying to learn/ get my head around immutable so I can set it up with React/ Redux. Here's my store setup:
store.js
const store = createStore(
rootReducer,
applyMiddleware(thunk)
)
combine reducers:
import { combineReducers } from 'redux';
import { UIreducer } from './UI-reducer';
export default combineReducers({
UIreducer
});
example reducer:
import { fromJS } from 'immutable';
const initialState = fromJS({
test: false
});
export const UIreducer = (state = initialState, action) => {
switch (action.type) {
case 'TEST': {
return state.set('test', true)
}
default: return state
}
}
I'm fairly sure i've set the above two parts up correctly, the only thing that i'm not sure about is how to map the state to props in my component:
Here's how I normally do it:
const mapStateToProps = state => ({
UI: state.UIreducer
})
When using immutable it returns an object that immutable generates which doesn't look like a normal state object if I were to not use immutable. I tried after a bit of research to use .get with the state like so:
const mapStateToProps = state => ({
UI: state.get('UIreducer')
})
This however returned the error message:
state.get is not a function
Could someone please point out where i've gone wrong?
Remember the state is the combination of all reducers, so the state will look like the combined reducers object
export default combineReducers({
UIreducer
});
At mapStateToProps you should ask for state.UIreducer and at the component level UI.get('test')
Answering further questions:
where exactly would I write the UI.get('test')?
Anywhere in your component UI will be a prop for it, you can use it like this
const myComponent = (props) => {
return <div>props.UI.get('test')</div>
}
I would have thought the .get('test') would go in the mapStateToProps somehwere.
That depends of which properties of your state you want to pass/map to your component, you can also pass an specifict
const mapStateToProps = state => ({
test: state.UIreducer.get('test')
})
and inside of you component
const myComponent = (props) => {
return <div>props.test</div>
}
You could use redux-immutable and convert the initialState to an immutable object:
import {
combineReducers
} from 'redux-immutable';
import {
createStore
} from 'redux';
const initialState = Immutable.Map();
const rootReducer = combineReducers({UIreducer});
I am getting an error while using combineReducer method in redux (redux#3.7.2). Same code will work when I am using only one reducer.
Running code here
Code
const { createStore, combineReducers, applyMiddleware } = require ('redux')
const aReducer = (state, action) => {
switch (action.type) {
case 'A':
{
return { ...state };
}
default: return state;
}
return state;
}
const bReducer = (state, action) => {
switch (action.type) {
case 'B':
{
return { ...state };
}
default: return state;
}
return state;
}
const configureStore = (initialState) => {
let rootReducer = combineReducers({ a:aReducer, b:bReducer });
console.log('configureStore', initialState);
const str = createStore(rootReducer, initialState);
return str;
};
const store = configureStore({});
console.log('store',store);
store.subscribe(() => {
console.log(store.getState());
});
In the create store line, if i am replacing the rootReducer to aReducer, this code wont have any problem. I did not understand why the reducers returning undefined state, i am passing initial state as a plane object.
There are two things going on here. Firstly, combineReducers also combines the states of each reducer in an object with the same keys as the argument reducers object, so to initialize each state correctly you'll need:
const store = configureStore({a: {}, b: {}});
This is not enough to fix the problem though, as combineReducers also requires that each reducer can handle state undefined and never returns undefined
(see the docs). If it can't you get this error:
Error: Reducer "..." returned undefined during initialization. If the state passed to the
reducer is undefined, you must explicitly return the initial state. The initial state may
not be undefined. If you don't want to set a value for this reducer, you can use null
instead of undefined.
The confusing thing about this is that the check is done when combineReducers is called (ie. before state initialization,) but the error isn't shown until the reducer is used. This means that even if you initialize the state correctly, and your reducers never receive state undefined, you'll still get the error if the reducers can't handle it.
To fix it, replace (state, action) => { in each reducer by (state = {}, action) => {, or explicitly return null if state is undefined. Note that these initial states are only used if you don't pass an initial state to createStore. To prevent confusion, I usually do all initialization in the reducers and not at createStore.