Redux Configs
Redux helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test.
How to use
Here we use the Redux variant family Redux, react-redux, redux-thunk, redux-persist, next-redux-wrapper for handle all state managements system. The installation method is as follows below.
Step 1: Set up STORE configs
Adding file & code in ./src/lib/redux/store.js
. This is configs supported for handle SSR state & CSR state.
import { createStore, applyMiddleware } from 'redux';
import { createWrapper } from 'next-redux-wrapper';
import thunkMiddleware from 'redux-thunk';
import combinedReducer from './reducers';
// BINDING MIDDLEWARE
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension');
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const makeStore = ({ isServer }) => {
if (isServer) {
//If it's on server side, create a store
return createStore(combinedReducer, bindMiddleware([thunkMiddleware]));
} else {
//If it's on client side, create a store which will persist
const { persistStore, persistReducer } = require('redux-persist');
const storage = require('redux-persist/lib/storage').default;
const persistConfig = {
key: process.env.NEXT_PUBLIC_APP_NAME ? process.env.NEXT_PUBLIC_APP_NAME : 'nextjs',
whitelist: ['register', 'auth'], // only counter will be persisted, add other reducers if needed
storage, // if needed, use a safer storage
};
const persistedReducer = persistReducer(persistConfig, combinedReducer); // Create a new reducer with our existing reducer
const store = createStore(persistedReducer, bindMiddleware([thunkMiddleware])); // Creating the store again
store.__persistor = persistStore(store); // This creates a persistor object & push that persisted object to .__persistor, so that we can avail the persistability feature
return store;
}
};
export const wrapper = createWrapper(makeStore);
Step 2: Set up reducers configs
Adding file & code in ./src/lib/redux/reducers.js
.
import { combineReducers } from 'redux';
import count from 'store/example-redux/count/reducer';
import tick from 'store/example-redux/tick/reducer';
const combinedReducer = combineReducers({
count,
tick,
});
export default combinedReducer;
Step 3: Init actions & reducers
We have 2 examples related to using a reducer. Adding action & reducer in
First we make count reducer. Adding file & code in ./src/store/example-redux/count/action.js
& ./src/store/example-redux/count/reducer.js
action.js
export const countActionTypes = {
ADD: 'ADD',
};
export const addCount = () => (dispatch) => {
return dispatch({ type: countActionTypes.ADD });
};
reducer.js
import { countActionTypes } from './action';
const countInitialState = {
count: 0,
};
export default function reducer(state = countInitialState, action) {
switch (action.type) {
case countActionTypes.ADD:
return Object.assign({}, state, {
count: state.count + 1,
});
default:
return state;
}
}
Second we make tick reducer. Adding file & code in ./src/store/example-redux/tick/action.js
& ./src/store/example-redux/tick/reducer.js
action.js
export const tickActionTypes = {
TICK: 'TICK',
};
export const serverRenderClock = (isServer) => (dispatch) => {
return dispatch({
type: tickActionTypes.TICK,
light: !isServer,
ts: Date.now(),
});
};
export const startClock = () => (dispatch) => {
return setInterval(() => dispatch({ type: tickActionTypes.TICK, light: true, ts: Date.now() }), 1000);
};
reducer.js
import { tickActionTypes } from './action';
const tickInitialState = {
lastUpdate: 0,
light: false,
};
export default function reducer(state = tickInitialState, action) {
switch (action.type) {
case tickActionTypes.TICK:
return Object.assign({}, state, {
lastUpdate: action.ts,
light: !!action.light,
});
default:
return state;
}
}
Step 4: Implement store wrapper
Adding code in ./src/pages/_app.js
// Vendors
import { useStore } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
// Configs
import { wrapper } from '@/redux/store';
const App = (props) => {
const store = useStore((state) => state);
...
return (
...
<PersistGate persistor={store.__persistor} loading={null}>
...
</PersistGate>
...
)
};
export default wrapper.withRedux(App);
Step 5: Implement in pages route Next.js
For test SSR mode. Adding file & code in ./src/pages/example-redux/ssr.js
import { useEffect } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Page from '@/hoc/example-redux/Page';
import { addCount } from 'store/example-redux/count/action';
import { wrapper } from '@/redux/store';
import { serverRenderClock, startClock } from 'store/example-redux/tick/action';
const Other = (props) => {
useEffect(() => {
const timer = props.startClock();
return () => {
clearInterval(timer);
};
}, [props]);
return <Page title="Other Page" linkTo="ssg" />;
};
export const getServerSideProps = wrapper.getServerSideProps(async ({ store }) => {
store.dispatch(serverRenderClock(true));
store.dispatch(addCount());
});
const mapDispatchToProps = (dispatch) => {
return {
addCount: bindActionCreators(addCount, dispatch),
startClock: bindActionCreators(startClock, dispatch),
};
};
export default connect(null, mapDispatchToProps)(Other);
For test CSR or SSG mode. Adding file & code in ./src/pages/example-redux/ssg.js
import { useEffect } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Page from '@/hoc/example-redux/Page';
import { addCount } from 'store/example-redux/count/action';
import { wrapper } from '@/redux/store';
import { serverRenderClock, startClock } from 'store/example-redux/tick/action';
const Index = (props) => {
useEffect(() => {
const timer = props.startClock();
return () => {
clearInterval(timer);
};
}, [props]);
return <Page title="Index Page" linkTo="ssr" />;
};
export const getStaticProps = wrapper.getStaticProps(async ({ store }) => {
store.dispatch(serverRenderClock(true));
store.dispatch(addCount());
});
const mapDispatchToProps = (dispatch) => {
return {
addCount: bindActionCreators(addCount, dispatch),
startClock: bindActionCreators(startClock, dispatch),
};
};
export default connect(null, mapDispatchToProps)(Index);
Last updated
Was this helpful?