Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add redux toolkit example with Typescript #23052

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions examples/with-redux-toolkit-typescript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

# vercel
.vercel
25 changes: 25 additions & 0 deletions examples/with-redux-toolkit-typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Redux Toolkit Typescript example

This is the existing [with-redux-toolkit](https://github.com/vercel/next.js/tree/canary/examples/with-redux-toolkit) plus TypeScript.

This example shows how to integrate Next.js with [Redux Toolkit](https://redux-toolkit.js.org).

The **Redux Toolkit** is intended to be the standard way to write Redux logic (create actions and reducers, setup the store with some default middlewares like redux devtools extension). This example demonstrates each of these features with Next.js

## Deploy your own

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-redux-toolkit-typescript&project-name=with-redux-toolkit-typescript&repository-name=with-redux-toolkit-typescript)

## How to use

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:

```bash
npx create-next-app --example with-redux-toolkit-typescript with-redux-toolkit-typescript-app
# or
yarn create next-app --example with-redux-toolkit-typescript with-redux-toolkit-typescript-app
```

Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
2 changes: 2 additions & 0 deletions examples/with-redux-toolkit-typescript/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
23 changes: 23 additions & 0 deletions examples/with-redux-toolkit-typescript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "with-redux-toolkit",
"version": "1.0.0",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@reduxjs/toolkit": "1.5.0",
"next": "10.0.8",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-redux": "7.2.2"
},
"license": "MIT",
"devDependencies": {
"@types/node": "14.14.34",
"@types/react": "17.0.3",
"@types/react-redux": "7.1.16",
"typescript": "4.2.3"
}
}
Binary file not shown.
1 change: 1 addition & 0 deletions examples/with-redux-toolkit-typescript/public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions examples/with-redux-toolkit-typescript/src/app/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import { AppDispatch, RootState } from './store'

export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
export const useAppDispatch = () => useDispatch<AppDispatch>()
17 changes: 17 additions & 0 deletions examples/with-redux-toolkit-typescript/src/app/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

export const store = configureStore({
reducer: {
counter: counterReducer,
},
})

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
.row {
display: flex;
align-items: center;
justify-content: center;
}

.row:not(:last-child) {
margin-bottom: 16px;
}

.value {
font-size: 78px;
padding-left: 16px;
padding-right: 16px;
margin-top: 2px;
font-family: 'Courier New', Courier, monospace;
}

.button {
appearance: none;
background: none;
font-size: 32px;
padding-left: 12px;
padding-right: 12px;
outline: none;
border: 2px solid transparent;
color: rgb(112, 76, 182);
padding-bottom: 4px;
cursor: pointer;
background-color: rgba(112, 76, 182, 0.1);
border-radius: 2px;
transition: all 0.15s;
}

.textbox {
font-size: 32px;
padding: 2px;
width: 64px;
text-align: center;
margin-right: 8px;
}

.button:hover,
.button:focus {
border: 2px solid rgba(112, 76, 182, 0.4);
}

.button:active {
background-color: rgba(112, 76, 182, 0.2);
}

.asyncButton {
composes: button;
position: relative;
margin-left: 8px;
}

.asyncButton:after {
content: '';
background-color: rgba(112, 76, 182, 0.15);
display: block;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
opacity: 0;
transition: width 1s linear, opacity 0.5s ease 1s;
}

.asyncButton:active:after {
width: 0%;
opacity: 1;
transition: 0s;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useState } from 'react'
import { useAppDispatch, useAppSelector } from '../../app/hooks'
import styles from './Counter.module.css'
import {
decrement,
increment,
incrementAsync,
incrementByAmount,
selectCount,
} from './counterSlice'

export function Counter() {
const count = useAppSelector(selectCount)
const dispatch = useAppDispatch()
const [incrementAmount, setIncrementAmount] = useState('2')

return (
<div>
<div className={styles.row}>
<button
className={styles.button}
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
+
</button>
<span className={styles.value}>{count}</span>
<button
className={styles.button}
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
-
</button>
</div>
<div className={styles.row}>
<input
className={styles.textbox}
aria-label="Set increment amount"
value={incrementAmount}
onChange={(e) => setIncrementAmount(e.target.value)}
/>
<button
className={styles.button}
onClick={() =>
dispatch(incrementByAmount(Number(incrementAmount) || 0))
}
>
Add Amount
</button>
<button
className={styles.asyncButton}
onClick={() => dispatch(incrementAsync(Number(incrementAmount) || 0))}
>
Add Async
</button>
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { createSlice } from '@reduxjs/toolkit'
import { AppThunk, RootState } from '../../app/store'

interface CounterState {
value: number
}

const initialState: CounterState = {
value: 0,
}

export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based on those changes
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
// Use the PayloadAction type to declare the contents of `action.payload`
incrementByAmount: (state, action) => {
state.value += action.payload
},
},
})

export const { increment, decrement, incrementByAmount } = counterSlice.actions

// The function below is called a thunk and allows us to perform async logic. It
// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
// will call the thunk with the `dispatch` function as the first argument. Async
// code can then be executed and other actions can be dispatched
export const incrementAsync = (amount: number): AppThunk => (dispatch) => {
setTimeout(() => {
dispatch(incrementByAmount(amount))
}, 1000)
}

// The function below is called a selector and allows us to select a value from
// the state. Selectors can also be defined inline where they're used instead of
// in the slice file. For example: `useSelector((state: RootState) => state.counter.value)`
export const selectCount = (state: RootState) => state.counter.value

export default counterSlice.reducer
14 changes: 14 additions & 0 deletions examples/with-redux-toolkit-typescript/src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { AppProps } from 'next/app'
import { Provider } from 'react-redux'
import { store } from '../app/store'
import '../styles/globals.css'

function MyApp({ Component, pageProps }: AppProps) {
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
)
}

export default MyApp
59 changes: 59 additions & 0 deletions examples/with-redux-toolkit-typescript/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Counter } from '../features/counter/Counter'
import styles from '../styles/Home.module.css'
import Head from 'next/head'

export default function Home() {
return (
<div className={styles.container}>
<Head>
<title>Redux Toolkit</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<header className={styles.header}>
<img src="/logo.svg" className={styles.logo} alt="logo" />
<Counter />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<span>
<span>Learn </span>
<a
className={styles.link}
href="https://reactjs.org/"
target="_blank"
rel="noopener noreferrer"
>
React
</a>
<span>, </span>
<a
className={styles.link}
href="https://redux.js.org/"
target="_blank"
rel="noopener noreferrer"
>
Redux
</a>
<span>, </span>
<a
className={styles.link}
href="https://redux-toolkit.js.org/"
target="_blank"
rel="noopener noreferrer"
>
Redux Toolkit
</a>
,<span> and </span>
<a
className={styles.link}
href="https://react-redux.js.org/"
target="_blank"
rel="noopener noreferrer"
>
React Redux
</a>
</span>
</header>
</div>
)
}
Loading