Skip to content

Commit

Permalink
implement redux toolkit in next js
Browse files Browse the repository at this point in the history
  • Loading branch information
dananw committed Mar 24, 2020
1 parent 12493ee commit 6c02540
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 481 deletions.
50 changes: 50 additions & 0 deletions lib/with-redux-store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react'
import {initializeStore} from '../store/store'

const isServer = typeof window === 'undefined'
const __NEXT_REDUX_STORE__ = '__NEXT_REDUX_STORE__'

function getOrCreateStore(initialState) {
// Always make a new store if server, otherwise state is shared between requests
if (isServer) {
return initializeStore(initialState)
}

// Create store if unavailable on the client and set it on the window object
if (!window[__NEXT_REDUX_STORE__]) {
window[__NEXT_REDUX_STORE__] = initializeStore(initialState)
}
return window[__NEXT_REDUX_STORE__]
}

export default App => {
return class AppWithRedux extends React.Component {
static async getInitialProps(appContext) {
// Get or Create the store with `undefined` as initialState
// This allows you to set a custom default initialState
const reduxStore = getOrCreateStore()

// Provide the store to getInitialProps of pages
appContext.ctx.reduxStore = reduxStore

let appProps = {}
if (typeof App.getInitialProps === 'function') {
appProps = await App.getInitialProps(appContext)
}

return {
...appProps,
initialReduxState: reduxStore.getState(),
}
}

constructor(props) {
super(props)
this.reduxStore = getOrCreateStore(props.initialReduxState)
}

render() {
return <App {...this.props} reduxStore={this.reduxStore} />
}
}
}
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
"start": "next start"
},
"dependencies": {
"@reduxjs/toolkit": "^1.2.5",
"next": "9.3.1",
"react": "16.13.1",
"react-dom": "16.13.1"
"react-dom": "16.13.1",
"react-redux": "^7.2.0",
"redux": "^4.0.5"
}
}
30 changes: 30 additions & 0 deletions pages/_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react";
import NextApp from "next/app";
import withReduxStore from '../lib/with-redux-store'
import { Provider } from 'react-redux'

class App extends NextApp {
static async getInitialProps({ Component, ctx }) {
let pageProps = {};

if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}

return {
pageProps,
};
}

render() {
const { Component, pageProps, reduxStore } = this.props;

return (
<Provider store={reduxStore}>
<Component {...pageProps} />
</Provider>
);
}
}

export default withReduxStore(App)
227 changes: 24 additions & 203 deletions pages/index.js
Original file line number Diff line number Diff line change
@@ -1,203 +1,24 @@
import Head from 'next/head'

const Home = () => (
<div className="container">
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>

<main>
<h1 className="title">
Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>

<p className="description">
Get started by editing <code>pages/index.js</code>
</p>

<div className="grid">
<a href="https://nextjs.org/docs" className="card">
<h3>Documentation &rarr;</h3>
<p>Find in-depth information about Next.js features and API.</p>
</a>

<a href="https://nextjs.org/learn" className="card">
<h3>Learn &rarr;</h3>
<p>Learn about Next.js in an interactive course with quizzes!</p>
</a>

<a
href="https://github.com/zeit/next.js/tree/master/examples"
className="card"
>
<h3>Examples &rarr;</h3>
<p>Discover and deploy boilerplate example Next.js projects.</p>
</a>

<a
href="https://zeit.co/import?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className="card"
>
<h3>Deploy &rarr;</h3>
<p>
Instantly deploy your Next.js site to a public URL with ZEIT Now.
</p>
</a>
</div>
</main>

<footer>
<a
href="https://zeit.co?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by <img src="/zeit.svg" alt="ZEIT Logo" />
</a>
</footer>

<style jsx>{`
.container {
min-height: 100vh;
padding: 0 0.5rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main {
padding: 5rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
footer {
width: 100%;
height: 100px;
border-top: 1px solid #eaeaea;
display: flex;
justify-content: center;
align-items: center;
}
footer img {
margin-left: 0.5rem;
}
footer a {
display: flex;
justify-content: center;
align-items: center;
}
a {
color: inherit;
text-decoration: none;
}
.title a {
color: #0070f3;
text-decoration: none;
}
.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}
.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
}
.title,
.description {
text-align: center;
}
.description {
line-height: 1.5;
font-size: 1.5rem;
}
code {
background: #fafafa;
border-radius: 5px;
padding: 0.75rem;
font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono,
DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
}
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
margin-top: 3rem;
}
.card {
margin: 1rem;
flex-basis: 45%;
padding: 1.5rem;
text-align: left;
color: inherit;
text-decoration: none;
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
}
.card:hover,
.card:focus,
.card:active {
color: #0070f3;
border-color: #0070f3;
}
.card h3 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
.card p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
@media (max-width: 600px) {
.grid {
width: 100%;
flex-direction: column;
}
}
`}</style>

<style jsx global>{`
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
* {
box-sizing: border-box;
}
`}</style>
</div>
)

export default Home
import React from 'react'
import {connect} from 'react-redux'
import {increment, decrement} from '../store/count/countSlice'

const IndexPage = ({counted, increment, decrement}) => {
return(
<div>
<h1>Simple implement Next.js With Redux Toolkit</h1>
<h2>Counted: {counted}</h2>
<hr/>
<button onClick={() => increment()}>Increment</button>
&nbsp;
<button onClick={() => decrement()}>decrement</button>
</div>
)
}

const mapStateToProps = (state) => ({
counted: state.count.count
})

const mapDispatchToProps = { increment, decrement }

export default connect(mapStateToProps, mapDispatchToProps)(IndexPage)
22 changes: 22 additions & 0 deletions store/count/countSlice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {createSlice} from '@reduxjs/toolkit'

let initialState = {
count: 0,
}

const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment(state, action) {
state.count += 1
},
decrement(state, action) {
state.count -= 1
},
}
})

export const { increment, decrement } = counterSlice.actions

export default counterSlice.reducer
9 changes: 9 additions & 0 deletions store/reducers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { combineReducers } from 'redux'

import count from './count/countSlice'

const reducers = combineReducers({
count,
})

export default reducers;
8 changes: 8 additions & 0 deletions store/store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {configureStore} from '@reduxjs/toolkit'
import rootReducers from './reducers'

export const initializeStore = () => {
return configureStore({
reducer: rootReducers
})
}
Loading

0 comments on commit 6c02540

Please sign in to comment.