Redux en Next.js
Instalar Redux
npm add -S redux react-redux next-redux-wrapper redux-devtools-extension redux-thunk
redux
: Librería para manejar el estado de aplicaciones JavaScript.react-redux
: Wrapper de Redux para React.next-redux-wrapper
: Wrapper de Redux para Next.js.redux-devtools-extension
: Wrapper de Redux DevTools Extension.redux-thunk
: Thunk middleware para Redux.
Configurar Redux en Next.js
- Añadimos el wrapper
next-redux-wrapper
al componentepages/_app
.
// pages/_app
/* Vendor */
// React and Redux.
import React from 'react';
import { Provider } from 'react-redux';
// Next.
import App, { Container } from 'next/app';
import withRedux from 'next-redux-wrapper';
/* Redux Store */
import { makeStore } from 'store';
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {};
return { pageProps };
}
render() {
const { Component, pageProps, store } = this.props;
return (
<Container>
<Provider store={store}>
<Component {...pageProps}/>
</Provider>
</Container>
);
}
}
export default withRedux(makeStore)(MyApp);
La función withRedux
toma makeStore
como primer argumento, y el resto de argumentos (opcionales) serán pasados a la función react-redux-connect
.
// src/store/index.js
/* Vendor */
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunkMiddleware from 'redux-thunk';
/* Reducer */
import reducer, { exampleInitialState } from 'reducers';
const makeStore = (initialState = exampleInitialState) => createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(thunkMiddleware)),
);
export default makeStore;
La función makeStore
recibirá el objeto initialState
como primer argumento, que es el estado inicial que le pasa el servidor al cliente. Esta función retornará una nueva instancia de la Redux store.
// src/reducers/index.js
/* Action types */
import { actionTypes } from 'actions';
export const exampleInitialState = {
lastUpdate: 0,
light: false,
count: 0,
};
export default (state = exampleInitialState, action) => {
switch (action.type) {
case actionTypes.TICK:
return Object.assign({}, state, {
lastUpdate: action.ts,
light: !!action.light,
});
case actionTypes.ADD:
return Object.assign({}, state, {
count: state.count + 1,
});
default:
return state;
}
};
// src/actions/index.js
export const actionTypes = {
ADD: 'ADD',
TICK: 'TICK',
};
export const addCount = () => dispatch => {
return dispatch({ type: actionTypes.ADD });
};
export const serverRenderClock = isServer => dispatch => {
return dispatch({ type: actionTypes.TICK, light: !isServer, ts: Date.now() });
};
export const startClock = () => dispatch => {
return setInterval(
() => dispatch({ type: actionTypes.TICK, light: true, ts: Date.now() }),
1000,
);
};