Redux actions to reducers not showing in devtools state - redux

I'd managed to get some of my earlier functions state in devtools as below:
Reducers function in DevTools
But when I tried to query some of the events in my interactions, the functions state werent able to display it. Below are my codes and settings, basically the flow is interactions > actions > reducers
interaction code:
export const loadAllOrders = async (exchange, dispatch) => {
// Fetch cancelled orders with the "Cancel" event stream
const fromBlock = 0;
const toBlock = "latest";
const cancelFilter = exchange.filters.CancelOrder();
const cancelStream = await exchange.queryFilter(cancelFilter, fromBlock, toBlock);
console.log(cancelStream)
// Format cancelled orders
const cancelledOrders = cancelStream.map((event) => event.args);
// Add cancelled orders to the redux store
dispatch(cancelledOrdersLoaded(cancelledOrders));
}
from my actions:
export const cancelledOrdersLoaded = (cancelledOrders) => {
return {
type: 'CANCELLED_ORDERS_LOADED',
payload:cancelledOrders
}
}
from my reducers:
export const exchange = (state = initialState, action) => {
switch (action.type) {
case 'EXCHANGE_LOADED':
return { ...state, loaded:true, contract: action.payload }
case 'CANCELLED_ORDERS_LOADED':
return { ...state, cancelledOrders: action.payload }
default:
return state
}
my configureStore
// For redux dev tools
const devTools = window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
const store = createStore(
rootReducer,
compose(applyMiddleware(thunk),devTools)
)
Thanks in advance

I haven't worked with redux for quite some time now, but from a quick look at some of my older repos, it seems like you didn't set up your store correctly.
This is what I have there,
import { applyMiddleware, createStore, compose, combineReducers } from "redux"
import thunk from "redux-thunk"
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const rootReducer = combineReducers({
reducers...
})
export const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)))

Related

React-redux Toolkit: Cannot set new state, when passing reducer as prop to another function

I am trying to use react redux toolkit and pass setter function to set new state on firebase's 'onAuthStateChanged'. The plan was to pass user's state (object or null) to reducer, depending if user is logged in or logged out. This is my first usage of redux, so I can't get why my code doesn't work. There is no errors, but in redux devtools state is always equal to null.
Configure Store:
import { configureStore } from '#reduxjs/toolkit'
import { Provider } from 'react-redux';
import userReducer from './utils/userReducer';
const store = configureStore({
reducer: {
user: userReducer,
}
})
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
My reducer:
import { createSlice } from "#reduxjs/toolkit";
export const userSlice = createSlice({
name: 'user',
initialState: null,
reducers: {
setUser: (state, action) => {
state = action.payload;
}
}
})
export const {setUser} = userSlice.actions;
export default userSlice.reducer;
Where I am dispatching it:
import { setUser } from '../utils/userReducer'
import { useDispatch } from 'react-redux'
const dispatch = useDispatch()
const handleLogin = async (e) => {
e.preventDefault()
const { user } = await logInWithEmail(email, password)
await setCurrentUser(() => dispatch(setUser))
}
Firebase function, where I am trying to use reducer:
export const setCurrentUser = async (setUser) => {
await onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser)
})
}
I understand, that with useContext it would be much easier, but I am trying to learn redux by implying it.
Try like that:
import { setUser } from '../utils/userReducer'
import { useDispatch } from 'react-redux'
const dispatch = useDispatch()
const handleLogin = async (e) => {
e.preventDefault()
const { user } = await logInWithEmail(email, password)
// This line updated
await setCurrentUser((currentUser) => dispatch(setUser(currentUser)))
}
The reason:
your setCurrentUser function prop setUser is just function () => dispatch(setUser), but this function does not receive any prop, and dispatch(setUser) does not do anything. you need to pass value (payload) to reducer function.
Additionally, try passing dispatch itself as prop and dispatch inside of onAuthStateChanged.
export const setCurrentUser = async (dispatch) => {
await onAuthStateChanged(auth, (currentUser) => {
dispatch(setUser(currentUser))
})
}
import setUser reducer function if handleLogin and setCurrentUser function is in different files separately.

Is it possible to generate static pages in nextjs when using redux-saga?

Warning: You have opted-out of Automatic Static Optimization due to
getInitialProps in pages/_app. This does not opt-out pages with
getStaticProps
I tried different options, but I can’t achieve static page generation, even if I take out the functionality I need from getInitialProps from_app, then wrapping it in withRedux, I still get it in the end. I tried with this - https://github.com/kirill-konshin/next-redux-wrapper - but could not get the result, I assume that this is because of the redux-saga and the whole application will use getInitialProps
/store.js
const ReduxStore = (initialState /*, options */) => {
const sagaMiddleware = createSagaMiddleware();
const middleware = [sagaMiddleware];
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(...middleware)
// other store enhancers if any
);
const store = createStore(
rootReducer,
initialState,
enhancer
);
store.runSaga = () => {
// Avoid running twice
if (store.saga) return;
store.saga = sagaMiddleware.run(saga);
};
store.stopSaga = async () => {
// Avoid running twice
if (!store.saga) return;
store.dispatch(END);
await store.saga.done;
store.saga = null;
// log('Stop Sagas');
};
store.execSagaTasks = async (ctx, tasks) => {
// run saga
await store.runSaga();
// dispatch saga tasks
tasks(store.dispatch);
// Stop running and wait for the tasks to be done
await store.stopSaga();
// Re-run on client side
if (!ctx.isServer) {
store.runSaga();
}
};
store.runSaga();
return store;
};
export default ReduxStore;
//_app.js
import { Provider } from 'react-redux';
import withRedux from 'next-redux-wrapper';
import App from 'next/app';
class MyApp extends App {
render() {
const {Component, pageProps, store} = this.props;
return <Provider store={store}>
<Component {...pageProps}/>
</Provider>;
}
}
export default withRedux(makeStore)(MyApp);
Has anyone experienced this or have any ideas? I will be grateful for any help

How to implement redux-toolkit and next,js and not lose SSR

I'm trying to implement redux-toolkit in my Next.js project without losing the option of SSR for the data I'm fetching from an external API. I have followed the example in next.js GitHub but doing so led to not having SSR when fetching data in my redux slice. I would like to know how I can fetch data and save that data in the Redux state. this is what I've written:
this is the store.js file
export const store = configureStore({
reducer: {
users: usersReducer,
},
});
the _app.js file
import { Provider } from 'react-redux';
import { store } from '../app/store';
const MyApp = ({ Component, pageProps }) => {
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
);
};
export default MyApp;
the usersSlice.js file
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
const initialState = {
items: null,
status: 'idle',
error: null,
};
export const fetchUsers = createAsyncThunk('users/fetchUsers', async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users');
const users = await res.json();
return users;
});
const usersSlice = createSlice({
name: 'categories',
initialState,
reducers: {},
extraReducers: {
[fetchUsers.pending]: (state, action) => {
state.status = 'loading';
},
[fetchUsers.fulfilled]: (state, action) => {
state.status = 'succeeded';
state.items = action.payload;
},
[fetchUsers.rejected]: (state, action) => {
state.status = 'failed';
state.error = action.error.message;
},
},
});
export default usersSlice.reducer;
and finally where the page I'm fetching the data from:
export default function Home() {
const dispatch = useDispatch();
const users = useSelector((state) => state.users.items);
useEffect(() => {
dispatch(fetchUsers());
}, [dispatch]);
return (
<div>
<h1>users</h1>
{users &&
users.length &&
users.map((user) => <p key={user.id}>{user.name}</p>)}
</div>
);
}
If I fetch data through dispatching the fetchUsers function it won't have SSR, and if I use getServerSideProps it won't be saved in redux state. I'm clueless
If you are okay to use Redux instead of redux-toolkit then follow this example
[https://github.com/vercel/next.js/blob/canary/examples/with-redux-persist]Officail Example form Vercel/next.js. I too facing some issue when i am writing SSR code for Redux persist with redux toolkit. The work is currently in progress. Will share the code when it is available. Sis

How to combine state sanitizer with existing middleware in React-Redux

My redux store is fairly large; Redux Devtools suggests sanitizing my larger objects to improve performance.
I've followed the docs here: https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/Troubleshooting.md#excessive-use-of-memory-and-cpu
I've tried a number of combinations here, but none have given me the output I expect.
The current version, seen below, results in state being returned as a function, not an object. I know I'm doing something wrong, but I'm not sure what. Any guidance would be deeply appreciated.
Here's my store.js:
'use strict'
// libraries
import { createStore, applyMiddleware, compose } from 'redux'
// middleware
import logger from 'redux-logger'
import thunk from 'redux-thunk'
// reducers
import reducer from './reducers'
const withLogger = false ? (thunk, logger) : thunk
const isProd = process.env.NODE_ENV === 'production'
const middleware = isProd ? thunk : withLogger
const composeEnhancers = isProd
? compose
: window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
// sanitizers to keep redux devtools from using excessive memory
const actionSanitizer = action =>
!!action.id
&& action.type === `RECEIVE_${action.id.toString().toUpperCase()}_COLLECTION`
? { ...action, data: '<<LONG_BLOB>>' }
: action
const store = createStore(
reducer,
composeEnhancers(applyMiddleware(middleware)),
// The addition of this code breaks my store
window.__REDUX_DEVTOOLS_EXTENSION__
&& window.__REDUX_DEVTOOLS_EXTENSION__({
actionSanitizer,
stateSanitizer: state =>
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state
})
// End breaking code
)
Second try
I've made a couple of updates, and can now see the sanitizers' effect in devtools - depending on placement in my createStore function. Unfortunately this changes my composeEnhancers behavior (fires, or does doesn't fire depending on placement)
// middleware with or without logger
const middlewareEnhancer =
true || ENV === 'production' // change to false to prevent logger output
? applyMiddleware(thunk, logger)
: applyMiddleware(thunk)
// sanitizers to keep redux devtools from using excessive memory
const actionSanitizer = action =>
!!action.id
&& action.type === `RECEIVE_${action.id.toString().toUpperCase()}_COLLECTION`
? { ...action, data: '<<LONG_BLOB>>' }
: action
// compose
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__(middlewareEnhancer) ||
compose(middlewareEnhancer)
const store = createStore(
// createStore signature > reducer, preLoadedState, enhancer
rootReducer,
// devtools extension works when I place it here per the examples in docs
// BUT composed enhancers fail
// Obviously, since the format wouldn't match the createStore signature
// I have no idea how `__REDUX_DEVTOOLS_EXTENSION__` should be used in conjunction with composeEnhancers
undefined,
composeEnhancers,
// devtools extension fails when placed here
// composed enhancers run
window.__REDUX_DEVTOOLS_EXTENSION__
&& window.__REDUX_DEVTOOLS_EXTENSION__({
actionSanitizer,
stateSanitizer: state =>
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state
})
)
Finally, persistence ftw!
I hate giving up; figured it out after rereading all the documentation posted by #markerikson. Always read the docs :'(
This may not be of use to anyone using configureStore and Redux Toolkit, but I'm documenting it regardless.
My big mistake was that actionSanitizer and stateSanitizer are Devtools Extension options, and should be added as such. Feel a fool, but at least I won't forget it.
The only thing left to do is implement redux-devtools-extension to avoid using window.__SOMEFUNC__ as suggested by markerikson.
The actual solution:
'use strict'
// libraries
import { createStore, applyMiddleware, compose } from 'redux'
// middleware
import logger from 'redux-logger'
import thunk from 'redux-thunk'
// reducers
import rootReducer from './reducers'
// middleware with or without logger
const middlewareEnhancer =
true || ENV === 'production' // change to false to prevent logger output
? applyMiddleware(thunk, logger)
: applyMiddleware(thunk)
// sanitizers to keep redux devtools from using excessive memory
const actionSanitizer = action =>
!!action.id
&& action.type === `RECEIVE_${action.id.toString().toUpperCase()}_COLLECTION`
? { ...action, data: '<<LONG_BLOB>>' }
: action
// compose
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// add sanitizers here as devtools options
// see https://github.com/zalmoxisus/redux-devtools-extension/tree/94f7e53800f4665bddc9b7438c5cc75cfb4547cc#12-advanced-store-setup
// section 1.2
actionSanitizer,
stateSanitizer: state =>
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state
}) || compose
const enhancer = composeEnhancers(middlewareEnhancer)
const store = createStore(rootReducer, undefined, enhancer)
export default store
As a first observation, this line seems wrong:
const withLogger = false ? (thunk, logger) : thunk
I'd strongly encourage you to first switch over to using the configureStore function from our official Redux Toolkit package, which handles the store setup process for you. From there, you can still pass DevTools configuration options to configureStore() if desired.
Only to complete the answer for those using the redux toolkit, here is an example entry that works well for me.
const devToolsConfiguration = {
actionSanitizer: (action) => {
switch (true) {
case action.type.includes(RESOLVED):
return typeof action.payload !== 'undefined'
? { ...action, payload: '<<LONG_BLOB>>' }
: { ...action, results: '<<LONG_BLOB>>' };
/* ... more entries */
default:
return action;
}
},
stateSanitizer: (state) =>
state.data?.matrix
? { ...state, data: { ...state.data, matrix: '<<LONG_BLOB>>' } }
: state,
};
I then reference the configuration in the toolkit's configureStore function:
const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
thunk: false,
serializableCheck: false,
immutableCheck: false,
}).prepend(middlewares),
preloadedState: initialState,
devTools: devToolsConfiguration, // <<< here
});

Reducer can't receive data from action creators

I've got a problem with my reducer. I have created an action creator, reducer, and used 'react-redux' connect to combine both.
When I'm fireing my action, action creator logs, that he just received a new data, but reducer does not log anything (Reducer logs only 3 initial loops). Also, store.getState() which I'm display in console every 5 seconds shows null (which is initial state of my store). Could you help me to deal with the problem?
export const UPDATE_PACKAGE_JSON = 'UPDATE_PACKAGE_JSON';
export function setName(name){
console.log('Action creator just received a name', name);
return {
type: UPDATE_PACKAGE_JSON,
payload: name
}
}
Container
const mapDispatchToProps = (dispatch) =>
bindActionCreators({ setName }, dispatch);
export default connect(null, mapDispatchToProps)(ConfigurationForm) ;
Reducer
import { UPDATE_PACKAGE_JSON } from './../actions/index';
export const packageJson = function (state = null, action){
console.log(UPDATE_PACKAGE_JSON);
switch(action.type){
case UPDATE_PACKAGE_JSON:
return {...state,
name: action.payload};
}
return state;
};
EDIT
Store
import { createStore } from 'redux';
import rootReducer from './../reducers/rootReducer';
const store = createStore(rootReducer);
export default store;
rootReducer
import { combineReducers } from 'redux';
import { packageJson } from './packageJson';
const rootReducer = combineReducers({
packageJson
});
export default rootReducer;

Resources