├── .gitignore ├── README.md ├── babel.config.json ├── components ├── App.js ├── Clock.tsx ├── ClockTime.tsx ├── ErrorMessage.js ├── Header.js ├── InfoBox.js ├── PostList.js ├── PostUpvoter.js └── Submit.js ├── layouts └── MyLayout.js ├── lib ├── apolloClient.js └── theme.js ├── next-env.d.ts ├── package.json ├── pages ├── _app.tsx ├── _document.js ├── about.js ├── client-only.js ├── index.js ├── material.tsx └── mobx-ssr.tsx ├── store.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Server side rendering boilerplate with Next.js V10.0 ,Apollo, MobX, Styled Components and Material-ui 2 | 3 | This project is a simple boilerplate for server side rendering approach. It comes with `Next.js` to handle routes and server side rendering, `Material-ui` for a components framework, `Styled-components` to style lib,`Apollo` for graphql, `MobX` for state management . 4 | 5 | ## Installing / Getting started 6 | 7 | Download the example [or clone the repo](https://github.com/msreekm/nextjs-material-ui-styled-components-boilerplate): 8 | 9 | ```shell 10 | git clone https://github.com/msreekm/nextjs-material-ui-styled-components-boilerplate 11 | npm install 12 | ``` 13 | 14 | ## Development 15 | 16 | To build the project and run the server in developer mode: 17 | 18 | ``` 19 | npm run dev 20 | ``` 21 | 22 | To run the tests: 23 | 24 | ``` 25 | npm test 26 | ``` 27 | 28 | ## Production 29 | 30 | To build the project and start the server to production: 31 | 32 | ``` 33 | npm run build && npm run start 34 | ``` 35 | 36 | ## Licensing 37 | 38 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 39 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": [ 4 | [ 5 | "styled-components", 6 | { "ssr": true, "displayName": true, "preprocess": false } 7 | ], 8 | ["@babel/plugin-proposal-decorators", { "legacy": true }], 9 | ["@babel/plugin-proposal-class-properties", { "loose": false }] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /components/App.js: -------------------------------------------------------------------------------- 1 | export default function App({ children }) { 2 | return ( 3 |
4 | {children} 5 | 45 |
46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /components/Clock.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Link from 'next/link' 3 | import { inject, observer } from 'mobx-react' 4 | import ClockTime from './ClockTime' 5 | 6 | @inject('store') 7 | @observer 8 | class Clock extends React.Component { 9 | componentDidMount() { 10 | this.props.store.start() 11 | } 12 | 13 | componentWillUnmount() { 14 | this.props.store.stop() 15 | } 16 | 17 | render() { 18 | return ( 19 |
20 |

{this.props.title}

21 | 25 | 30 |
31 | ) 32 | } 33 | } 34 | 35 | export default Clock 36 | -------------------------------------------------------------------------------- /components/ClockTime.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react' 2 | const ClockTime = observer((props) => { 3 | return ( 4 |
5 | {props.timeString} 6 | 18 |
19 | ) 20 | }) 21 | export default ClockTime 22 | -------------------------------------------------------------------------------- /components/ErrorMessage.js: -------------------------------------------------------------------------------- 1 | export default function ErrorMessage({ message }) { 2 | return ( 3 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /components/Header.js: -------------------------------------------------------------------------------- 1 | import { useRouter } from 'next/router' 2 | import Link from 'next/link' 3 | 4 | export default function Header() { 5 | const { pathname } = useRouter() 6 | 7 | return ( 8 |
9 | 10 | Home 11 | 12 | 13 | About 14 | 15 | 16 | 17 | Client-Only 18 | 19 | 20 | 21 | Material 22 | 23 | 36 |
37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /components/InfoBox.js: -------------------------------------------------------------------------------- 1 | const InfoBox = ({ children }) => ( 2 |
3 | 13 | {children} 14 |
15 | ) 16 | 17 | export default InfoBox 18 | -------------------------------------------------------------------------------- /components/PostList.js: -------------------------------------------------------------------------------- 1 | import { gql, useQuery, NetworkStatus } from '@apollo/client' 2 | import ErrorMessage from './ErrorMessage' 3 | import PostUpvoter from './PostUpvoter' 4 | 5 | export const ALL_POSTS_QUERY = gql` 6 | query allPosts($first: Int!, $skip: Int!) { 7 | allPosts(orderBy: { createdAt: desc }, first: $first, skip: $skip) { 8 | id 9 | title 10 | votes 11 | url 12 | createdAt 13 | } 14 | _allPostsMeta { 15 | count 16 | } 17 | } 18 | ` 19 | 20 | export const allPostsQueryVars = { 21 | skip: 0, 22 | first: 10, 23 | } 24 | 25 | export default function PostList() { 26 | const { loading, error, data, fetchMore, networkStatus } = useQuery( 27 | ALL_POSTS_QUERY, 28 | { 29 | variables: allPostsQueryVars, 30 | // Setting this value to true will make the component rerender when 31 | // the "networkStatus" changes, so we are able to know if it is fetching 32 | // more data 33 | notifyOnNetworkStatusChange: true, 34 | } 35 | ) 36 | 37 | const loadingMorePosts = networkStatus === NetworkStatus.fetchMore 38 | 39 | const loadMorePosts = () => { 40 | fetchMore({ 41 | variables: { 42 | skip: allPosts.length, 43 | }, 44 | }) 45 | } 46 | 47 | if (error) return 48 | if (loading && !loadingMorePosts) return
Loading
49 | 50 | const { allPosts, _allPostsMeta } = data 51 | const areMorePosts = allPosts.length < _allPostsMeta.count 52 | 53 | return ( 54 |
55 | 66 | {areMorePosts && ( 67 | 70 | )} 71 | 109 |
110 | ) 111 | } 112 | -------------------------------------------------------------------------------- /components/PostUpvoter.js: -------------------------------------------------------------------------------- 1 | import { gql, useMutation } from '@apollo/client' 2 | 3 | const UPDATE_POST_MUTATION = gql` 4 | mutation votePost($id: String!) { 5 | votePost(id: $id) { 6 | id 7 | votes 8 | __typename 9 | } 10 | } 11 | ` 12 | 13 | export default function PostUpvoter({ votes, id }) { 14 | const [updatePost] = useMutation(UPDATE_POST_MUTATION) 15 | 16 | const upvotePost = () => { 17 | updatePost({ 18 | variables: { 19 | id, 20 | }, 21 | optimisticResponse: { 22 | __typename: 'Mutation', 23 | votePost: { 24 | __typename: 'Post', 25 | id, 26 | votes: votes + 1, 27 | }, 28 | }, 29 | }) 30 | } 31 | 32 | return ( 33 | 56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /components/Submit.js: -------------------------------------------------------------------------------- 1 | import { gql, useMutation } from '@apollo/client' 2 | 3 | const CREATE_POST_MUTATION = gql` 4 | mutation createPost($title: String!, $url: String!) { 5 | createPost(title: $title, url: $url) { 6 | id 7 | title 8 | votes 9 | url 10 | createdAt 11 | } 12 | } 13 | ` 14 | 15 | export default function Submit() { 16 | const [createPost, { loading }] = useMutation(CREATE_POST_MUTATION) 17 | 18 | const handleSubmit = (event) => { 19 | event.preventDefault() 20 | const form = event.target 21 | const formData = new window.FormData(form) 22 | const title = formData.get('title') 23 | const url = formData.get('url') 24 | form.reset() 25 | 26 | createPost({ 27 | variables: { title, url }, 28 | update: (cache, { data: { createPost } }) => { 29 | cache.modify({ 30 | fields: { 31 | allPosts(existingPosts = []) { 32 | const newPostRef = cache.writeFragment({ 33 | data: createPost, 34 | fragment: gql` 35 | fragment NewPost on allPosts { 36 | id 37 | type 38 | } 39 | `, 40 | }) 41 | return [newPostRef, ...existingPosts] 42 | }, 43 | }, 44 | }) 45 | }, 46 | }) 47 | } 48 | 49 | return ( 50 |
51 |

Submit

52 | 53 | 54 | 57 | 71 |
72 | ) 73 | } 74 | -------------------------------------------------------------------------------- /layouts/MyLayout.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import Header from '../components/Header' 3 | 4 | export default function MyLayout({ children }) { 5 | const [counter, setCounter] = useState(0) 6 | 7 | return ( 8 | <> 9 |

10 |

Company Name

11 |

12 |

13 |

14 |

15 |

16 | 19 |

20 | 21 | {children} 22 | 62 | 63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /lib/apolloClient.js: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client"; 3 | import { concatPagination } from "@apollo/client/utilities"; 4 | import merge from "deepmerge"; 5 | import isEqual from "lodash/isEqual"; 6 | 7 | export const APOLLO_STATE_PROP_NAME = "__APOLLO_STATE__"; 8 | 9 | let apolloClient; 10 | 11 | function createApolloClient() { 12 | return new ApolloClient({ 13 | ssrMode: typeof window === "undefined", 14 | link: new HttpLink({ 15 | uri: "https://nextjs-graphql-with-prisma-simple.vercel.app/api", // Server URL (must be absolute) 16 | credentials: "same-origin", // Additional fetch() options like `credentials` or `headers` 17 | }), 18 | cache: new InMemoryCache({ 19 | typePolicies: { 20 | Query: { 21 | fields: { 22 | allPosts: concatPagination(), 23 | }, 24 | }, 25 | }, 26 | }), 27 | }); 28 | } 29 | 30 | export function initializeApollo(initialState = null) { 31 | const _apolloClient = apolloClient ?? createApolloClient(); 32 | 33 | // If your page has Next.js data fetching methods that use Apollo Client, the initial state 34 | // gets hydrated here 35 | if (initialState) { 36 | // Get existing cache, loaded during client side data fetching 37 | const existingCache = _apolloClient.extract(); 38 | 39 | // Merge the existing cache into data passed from getStaticProps/getServerSideProps 40 | const data = merge(initialState, existingCache, { 41 | // combine arrays using object equality (like in sets) 42 | arrayMerge: (destinationArray, sourceArray) => [ 43 | ...sourceArray, 44 | ...destinationArray.filter((d) => 45 | sourceArray.every((s) => !isEqual(d, s)) 46 | ), 47 | ], 48 | }); 49 | 50 | // Restore the cache with the merged data 51 | _apolloClient.cache.restore(data); 52 | } 53 | // For SSG and SSR always create a new Apollo Client 54 | if (typeof window === "undefined") return _apolloClient; 55 | // Create the Apollo Client once in the client 56 | if (!apolloClient) apolloClient = _apolloClient; 57 | 58 | return _apolloClient; 59 | } 60 | 61 | export function addApolloState(client, pageProps) { 62 | if (pageProps?.props) { 63 | pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract(); 64 | } 65 | 66 | return pageProps; 67 | } 68 | 69 | export function useApollo(pageProps) { 70 | const state = pageProps[APOLLO_STATE_PROP_NAME]; 71 | const store = useMemo(() => initializeApollo(state), [state]); 72 | return store; 73 | } 74 | -------------------------------------------------------------------------------- /lib/theme.js: -------------------------------------------------------------------------------- 1 | import { createMuiTheme } from '@material-ui/core/styles' 2 | import { red } from '@material-ui/core/colors' 3 | 4 | // Create a theme instance. 5 | const theme = createMuiTheme({ 6 | palette: { 7 | primary: { 8 | main: '#556cd6', 9 | }, 10 | secondary: { 11 | main: '#19857b', 12 | }, 13 | error: { 14 | main: red.A400, 15 | }, 16 | background: { 17 | default: '#fff', 18 | }, 19 | }, 20 | }) 21 | 22 | export default theme 23 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "with-apollo", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "dev": "next", 6 | "build": "next build", 7 | "start": "next start" 8 | }, 9 | "dependencies": { 10 | "@apollo/client": "3.1.1", 11 | "@material-ui/core": "latest", 12 | "clsx": "latest", 13 | "deepmerge": "^4.2.2", 14 | "graphql": "^15.3.0", 15 | "lodash": "4.17.20", 16 | "mobx": "^6.0.1", 17 | "mobx-react": "^7.0.0", 18 | "next": "latest", 19 | "prettier": "2.1.2", 20 | "prop-types": "^15.6.2", 21 | "react": "^16.7.0", 22 | "react-dom": "^16.7.0", 23 | "styled-components": "^5.0.0" 24 | }, 25 | "license": "MIT", 26 | "devDependencies": { 27 | "babel-plugin-styled-components": "^1.8.0", 28 | "@babel/plugin-proposal-class-properties": "^7.1.0", 29 | "@babel/plugin-proposal-decorators": "^7.1.0", 30 | "@types/node": "14.14.7", 31 | "@types/react": "16.9.56", 32 | "typescript": "4.0.5" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { ApolloProvider } from '@apollo/client' 2 | import { useApollo } from '../lib/apolloClient' 3 | import { useStore } from '../store' 4 | import { Provider as StoreProvider } from 'mobx-react' 5 | // import { ThemeProvider } from 'styled-components' 6 | import { useEffect } from 'react' 7 | import { ThemeProvider as StyledThemeProvider } from 'styled-components' 8 | import { 9 | ThemeProvider as MaterialThemeProvider, 10 | createMuiTheme, 11 | } from '@material-ui/core/styles' 12 | 13 | import CssBaseline from '@material-ui/core/CssBaseline' 14 | 15 | //custom theme 16 | import theme from '../lib/theme' 17 | 18 | export default function App({ Component, pageProps }) { 19 | console.log('app') 20 | const apolloClient = useApollo(pageProps) 21 | const store = useStore(pageProps.initialState) 22 | const Layout = Component.Layout ? Component.Layout : React.Fragment 23 | 24 | React.useEffect(() => { 25 | // Remove the server-side injected CSS. 26 | const jssStyles = document.querySelector('#jss-server-side') 27 | if (jssStyles) { 28 | jssStyles.parentElement.removeChild(jssStyles) 29 | } 30 | }, []) 31 | 32 | return ( 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /pages/_document.js: -------------------------------------------------------------------------------- 1 | import Document from 'next/document' 2 | import { ServerStyleSheet as StyledComponentSheets } from 'styled-components' 3 | import { ServerStyleSheets as MaterialUiServerStyleSheets } from '@material-ui/core/styles' 4 | import flush from 'styled-jsx/server' 5 | 6 | export default class MyDocument extends Document { 7 | static async getInitialProps(ctx) { 8 | const styledComponentSheet = new StyledComponentSheets() 9 | const materialUiSheets = new MaterialUiServerStyleSheets() 10 | const originalRenderPage = ctx.renderPage 11 | try { 12 | ctx.renderPage = () => 13 | originalRenderPage({ 14 | enhanceApp: (App) => (props) => 15 | styledComponentSheet.collectStyles( 16 | materialUiSheets.collect() 17 | ), 18 | }) 19 | const initialProps = await Document.getInitialProps(ctx) 20 | return { 21 | ...initialProps, 22 | styles: ( 23 | 24 | {initialProps.styles} 25 | {materialUiSheets.getStyleElement()} 26 | {styledComponentSheet.getStyleElement()} 27 | {flush() || null} 28 | 29 | ), 30 | } 31 | } finally { 32 | styledComponentSheet.seal() 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pages/about.js: -------------------------------------------------------------------------------- 1 | import App from '../components/App' 2 | import Header from '../components/Header' 3 | import MyLayout from '../layouts/MyLayout' 4 | 5 | const AboutPage = () => ( 6 | <> 7 |
8 |

The Idea Behind This Example

9 |

10 | Apollo is a GraphQL 11 | client that allows you to easily query the exact data you need from a 12 | GraphQL server. In addition to fetching and mutating data, Apollo 13 | analyzes your queries and their results to construct a client-side cache 14 | of your data, which is kept up to date as further queries and mutations 15 | are run, fetching more results from the server. 16 |

17 |

18 | In this simple example, we integrate Apollo seamlessly with{' '} 19 | Next by calling{' '} 20 | 21 | getStaticProps 22 | {' '} 23 | at our Page component. This approach lets us opt out of getInitialProps 24 | and let us use all the niceties provided by{' '} 25 | Next. 26 |

27 |

28 | On initial page load, while on the server and inside{' '} 29 | 30 | getStaticProps 31 | 32 | , we fetch the query used to get the list of posts. At the point in 33 | which the query promise resolves, our Apollo Client store is completely 34 | initialized. Then we serve the initial HTML with the fetched data and 35 | hydrate Apollo in the browser. 36 |

37 |

38 | This example relies on graph.cool for 39 | its GraphQL backend. 40 |

41 |
42 | 43 | ) 44 | AboutPage.Layout = MyLayout 45 | 46 | export default AboutPage 47 | -------------------------------------------------------------------------------- /pages/client-only.js: -------------------------------------------------------------------------------- 1 | import App from '../components/App' 2 | import InfoBox from '../components/InfoBox' 3 | import Header from '../components/Header' 4 | import Submit from '../components/Submit' 5 | import PostList from '../components/PostList' 6 | import MyLayout from '../layouts/MyLayout' 7 | 8 | const ClientOnlyPage = (props) => ( 9 | <> 10 | 11 | ℹ️ This page shows how use Apollo only in the client. If you{' '} 12 | reload this page, you will see a loader since 13 | Apollo didn't fetch any data on the server. This is useful when the page 14 | doesn't have SEO requirements or blocking data fetching requirements. 15 | 16 | 17 | 18 | 19 | ) 20 | 21 | ClientOnlyPage.Layout = MyLayout 22 | 23 | export default ClientOnlyPage 24 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import InfoBox from '../components/InfoBox' 2 | import Submit from '../components/Submit' 3 | import PostList, { 4 | ALL_POSTS_QUERY, 5 | allPostsQueryVars, 6 | } from '../components/PostList' 7 | import { initializeApollo, addApolloState } from '../lib/apolloClient' 8 | import styled from 'styled-components' 9 | import MyLayout from '../layouts/MyLayout' 10 | import Link from 'next/link' 11 | 12 | const Title = styled.h1` 13 | font-size: 22px; 14 | color: ${({ theme }) => theme.palette.secondary.main}; 15 | ` 16 | 17 | const IndexPage = () => ( 18 | <> 19 | Home page 20 | Client Only 21 | 22 | ℹ️ This page shows how to use SSG with Apollo. 23 | 24 | 25 | 26 | ) 27 | 28 | export async function getStaticProps() { 29 | const apolloClient = initializeApollo() 30 | 31 | await apolloClient.query({ 32 | query: ALL_POSTS_QUERY, 33 | variables: allPostsQueryVars, 34 | }) 35 | 36 | return addApolloState(apolloClient, { 37 | props: {}, 38 | revalidate: 1, 39 | }) 40 | } 41 | 42 | IndexPage.Layout = MyLayout 43 | 44 | export default IndexPage 45 | -------------------------------------------------------------------------------- /pages/material.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Container from '@material-ui/core/Container' 3 | import Typography from '@material-ui/core/Typography' 4 | import { Button, Paper } from '@material-ui/core' 5 | import styled from 'styled-components' 6 | import App from '../components/App' 7 | import Header from '../components/Header' 8 | import MyLayout from '../layouts/MyLayout' 9 | 10 | const MaterialContainer = styled(Container)` 11 | background-color: ${({ theme }) => theme.palette.secondary.main}; 12 | margin-top: 40px; 13 | padding: 40px; 14 | ` 15 | 16 | export default function Index() { 17 | return ( 18 | <> 19 | 20 | 21 | 22 | 25 | 28 | 31 | 34 | 35 | 36 | 37 | ) 38 | } 39 | Index.Layout = MyLayout 40 | -------------------------------------------------------------------------------- /pages/mobx-ssr.tsx: -------------------------------------------------------------------------------- 1 | import Clock from '../components/Clock' 2 | 3 | export default function MobXSSR() { 4 | return 5 | } 6 | 7 | // The date returned here will be different for every request that hits the page, 8 | // that is because the page becomes a serverless function instead of being statically 9 | // exported when you use `getServerSideProps` or `getInitialProps` 10 | export function getServerSideProps() { 11 | return { props: { initialState: { lastUpdate: Date.now() } } } 12 | } 13 | -------------------------------------------------------------------------------- /store.ts: -------------------------------------------------------------------------------- 1 | import { action, observable, computed, runInAction, makeObservable } from 'mobx' 2 | import { enableStaticRendering } from 'mobx-react' 3 | import { useMemo } from 'react' 4 | // eslint-disable-next-line react-hooks/rules-of-hooks 5 | enableStaticRendering(typeof window === 'undefined') 6 | 7 | let store 8 | 9 | class Store { 10 | constructor() { 11 | makeObservable(this) 12 | } 13 | 14 | @observable lastUpdate = 0 15 | @observable light = false 16 | 17 | @action start = () => { 18 | this.timer = setInterval(() => { 19 | runInAction(() => { 20 | this.lastUpdate = Date.now() 21 | this.light = true 22 | }) 23 | }, 1000) 24 | } 25 | 26 | @computed get timeString() { 27 | const pad = (n) => (n < 10 ? `0${n}` : n) 28 | const format = (t) => 29 | `${pad(t.getUTCHours())}:${pad(t.getUTCMinutes())}:${pad( 30 | t.getUTCSeconds() 31 | )}` 32 | return format(new Date(this.lastUpdate)) 33 | } 34 | 35 | stop = () => clearInterval(this.timer) 36 | 37 | hydrate = (data) => { 38 | if (!data) return 39 | 40 | this.lastUpdate = data.lastUpdate !== null ? data.lastUpdate : Date.now() 41 | this.light = !!data.light 42 | } 43 | } 44 | 45 | function initializeStore(initialData = null) { 46 | const _store = store ?? new Store() 47 | 48 | // If your page has Next.js data fetching methods that use a Mobx store, it will 49 | // get hydrated here, check `pages/ssg.js` and `pages/ssr.js` for more details 50 | if (initialData) { 51 | _store.hydrate(initialData) 52 | } 53 | // For SSG and SSR always create a new store 54 | if (typeof window === 'undefined') return _store 55 | // Create the store once in the client 56 | if (!store) store = _store 57 | 58 | return _store 59 | } 60 | 61 | export function useStore(initialState) { 62 | const store = useMemo(() => initializeStore(initialState), [initialState]) 63 | return store 64 | } 65 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve" 20 | }, 21 | "include": [ 22 | "next-env.d.ts", 23 | "**/*.ts", 24 | "**/*.tsx" 25 | ], 26 | "exclude": [ 27 | "node_modules" 28 | ] 29 | } 30 | --------------------------------------------------------------------------------