mirror of
https://github.com/sergeyyarkov/blog-example.git
synced 2026-10-11 02:08:57 +03:00
added SSR
This commit is contained in:
parent
6cdb91e767
commit
48c8d5d82b
Binary file not shown.
@ -1,21 +1,20 @@
|
||||
import Card from "./card";
|
||||
import Card from "./Card";
|
||||
|
||||
const Articles = ({ articles }) => {
|
||||
console.log(articles)
|
||||
const leftArticlesCount = Math.ceil(articles.length / 5);
|
||||
const leftArticles = articles.slice(0, leftArticlesCount);
|
||||
const rightArticles = articles.slice(leftArticlesCount, articles.length);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="uk-child-width-1-2" data-uk-grid>
|
||||
<div className="uk-child-width-1-2 uk-grid" data-uk-grid>
|
||||
<div>
|
||||
{leftArticles.map((article, i) => {
|
||||
return <Card article={article} key={`article__${article.id}`} />;
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
<div className="uk-child-width-1-2@m uk-grid-match" data-uk-grid>
|
||||
<div className="uk-child-width-1-2@m uk-grid-match uk-grid" data-uk-grid>
|
||||
{rightArticles.map((article, i) => {
|
||||
return <Card article={article} key={`article__${article.id}`} />;
|
||||
})}
|
||||
|
||||
27
frontend/components/Layout.js
Normal file
27
frontend/components/Layout.js
Normal file
@ -0,0 +1,27 @@
|
||||
import Head from 'next/head'
|
||||
import Nav from './Nav'
|
||||
|
||||
const Layout = props => {
|
||||
return(
|
||||
<>
|
||||
<Head>
|
||||
<title>{props.titlePage}</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css?family=Staatliches"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/css/uikit.min.css"
|
||||
/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.2.0/js/uikit.min.js" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/js/uikit-icons.min.js" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.2.0/js/uikit.js" />
|
||||
</Head>
|
||||
<Nav />
|
||||
{props.children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Layout
|
||||
@ -1,6 +1,7 @@
|
||||
import Link from "next/link"
|
||||
import Query from "../components/query";
|
||||
import Query from "./query";
|
||||
import CATEGORIES_QUERY from "../apollo/queries/category/categories.js";
|
||||
import { withApollo } from "../utils/apollo";
|
||||
|
||||
const Nav = () => {
|
||||
return (
|
||||
@ -9,7 +10,7 @@ const Nav = () => {
|
||||
{({ data: { categories } }) => {
|
||||
return (
|
||||
<div>
|
||||
<nav className="uk-container uk-container-large uk-navbar-container" data-uk-navbar>
|
||||
<nav className="uk-container uk-container-large uk-navbar-container uk-navbar" data-uk-navbar>
|
||||
<div className="uk-navbar-left">
|
||||
<ul className="uk-navbar-nav">
|
||||
<li>
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
require("dotenv").config();
|
||||
const withCSS = require('@zeit/next-css')
|
||||
const withFonts = require('next-fonts')
|
||||
|
||||
module.exports = withCSS({
|
||||
env: {
|
||||
API_URL: process.env.API_URL
|
||||
}
|
||||
},
|
||||
devIndicators: {
|
||||
autoPrerender: false,
|
||||
},
|
||||
})
|
||||
36
frontend/package-lock.json
generated
36
frontend/package-lock.json
generated
@ -82,6 +82,16 @@
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"@apollo/react-ssr": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@apollo/react-ssr/-/react-ssr-3.1.3.tgz",
|
||||
"integrity": "sha512-fUTmEYHxSTX1GA43B8vICxXXplpcEBnDwn0IgdAc3eG0p2YK97ZrJDRFCJ5vD7fyDZsrYhMf+rAI3sd+H2SS+A==",
|
||||
"requires": {
|
||||
"@apollo/react-common": "^3.1.3",
|
||||
"@apollo/react-hooks": "^3.1.3",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
|
||||
@ -1434,6 +1444,22 @@
|
||||
"picomatch": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"apollo-boost": {
|
||||
"version": "0.4.7",
|
||||
"resolved": "https://registry.npmjs.org/apollo-boost/-/apollo-boost-0.4.7.tgz",
|
||||
"integrity": "sha512-jfc3aqO0vpCV+W662EOG5gq4AH94yIsvSgAUuDvS3o/Z+8Joqn4zGC9CgLCDHusK30mFgtsEgwEe0pZoedohsQ==",
|
||||
"requires": {
|
||||
"apollo-cache": "^1.3.4",
|
||||
"apollo-cache-inmemory": "^1.6.5",
|
||||
"apollo-client": "^2.6.7",
|
||||
"apollo-link": "^1.0.6",
|
||||
"apollo-link-error": "^1.0.3",
|
||||
"apollo-link-http": "^1.3.1",
|
||||
"graphql-tag": "^2.4.2",
|
||||
"ts-invariant": "^0.4.0",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"apollo-cache": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.4.tgz",
|
||||
@ -1481,6 +1507,16 @@
|
||||
"zen-observable-ts": "^0.8.20"
|
||||
}
|
||||
},
|
||||
"apollo-link-error": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link-error/-/apollo-link-error-1.1.12.tgz",
|
||||
"integrity": "sha512-psNmHyuy3valGikt/XHJfe0pKJnRX19tLLs6P6EHRxg+6q6JMXNVLYPaQBkL0FkwdTCB0cbFJAGRYCBviG8TDA==",
|
||||
"requires": {
|
||||
"apollo-link": "^1.2.13",
|
||||
"apollo-link-http-common": "^0.2.15",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"apollo-link-http": {
|
||||
"version": "1.5.16",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.16.tgz",
|
||||
|
||||
@ -9,7 +9,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/react-hooks": "^3.1.3",
|
||||
"@apollo/react-ssr": "^3.1.3",
|
||||
"@zeit/next-css": "^1.0.1",
|
||||
"apollo-boost": "^0.4.7",
|
||||
"apollo-cache-inmemory": "^1.6.5",
|
||||
"apollo-client": "^2.6.8",
|
||||
"apollo-link-http": "^1.5.16",
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
import Head from "next/head";
|
||||
import NProgress from 'nprogress'
|
||||
import Router from 'next/router'
|
||||
import "../assets/css/style.css";
|
||||
import { ApolloProvider } from "@apollo/react-hooks";
|
||||
import withData from "../utils/apollo";
|
||||
|
||||
import Nav from '../components/nav'
|
||||
|
||||
Router.events.on('routeChangeStart', url => NProgress.start())
|
||||
Router.events.on('routeChangeComplete', () => NProgress.done())
|
||||
Router.events.on('routeChangeError', () => NProgress.done())
|
||||
|
||||
const App = ({ Component, pageProps, apollo }) => {
|
||||
return (
|
||||
<ApolloProvider client={apollo}>
|
||||
<Head>
|
||||
<title>Strapi blog</title>
|
||||
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css?family=Staatliches"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/css/uikit.min.css"
|
||||
/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.2.0/js/uikit.min.js" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/js/uikit-icons.min.js" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.2.0/js/uikit.js" />
|
||||
</Head>
|
||||
<Nav />
|
||||
<Component {...pageProps} />
|
||||
</ApolloProvider>
|
||||
)
|
||||
};
|
||||
|
||||
// Wraps all components in the tree with the data provider
|
||||
export default withData(App);
|
||||
@ -1,15 +1,20 @@
|
||||
import { useRouter } from "next/router";
|
||||
import Query from "../../components/query";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import Moment from "react-moment";
|
||||
import ARTICLE_QUERY from "../../apollo/queries/article/article";
|
||||
import { useRouter } from "next/router";
|
||||
import { withApollo } from "../../utils/apollo";
|
||||
|
||||
import Query from "../../components/query";
|
||||
import Layout from "../../components/Layout"
|
||||
|
||||
const Article = () => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<Query query={ARTICLE_QUERY} id={router.query.id}>
|
||||
{({ data: { article } }) => {
|
||||
return (
|
||||
<Layout titlePage={article.title}>
|
||||
<div>
|
||||
<div
|
||||
id="banner"
|
||||
@ -30,10 +35,11 @@ const Article = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}}
|
||||
</Query>
|
||||
);
|
||||
};
|
||||
|
||||
export default Article;
|
||||
export default withApollo(Article);
|
||||
@ -1,12 +1,16 @@
|
||||
import CATEGORY_ARTICLES_QUERY from "../../apollo/queries/category/articles";
|
||||
import { useRouter } from "next/router";
|
||||
import { withApollo } from "../../utils/apollo";
|
||||
|
||||
import Articles from "../../components/articles";
|
||||
import Query from "../../components/query";
|
||||
import CATEGORY_ARTICLES_QUERY from "../../apollo/queries/category/articles";
|
||||
import Layout from "../../components/Layout"
|
||||
|
||||
const Category = () => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<Layout titlePage="Categories">
|
||||
<Query query={CATEGORY_ARTICLES_QUERY} id={router.query.id}>
|
||||
{({ data: { category } }) => {
|
||||
return (
|
||||
@ -21,7 +25,8 @@ const Category = () => {
|
||||
);
|
||||
}}
|
||||
</Query>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Category
|
||||
export default withApollo(Category)
|
||||
@ -1,9 +1,12 @@
|
||||
import Layout from "../components/Layout"
|
||||
import Articles from "../components/articles";
|
||||
import Query from "../components/query";
|
||||
import ARTICLES_QUERY from "../apollo/queries/article/articles";
|
||||
import { withApollo } from '../utils/apollo';
|
||||
|
||||
const Home = () => {
|
||||
return (
|
||||
<Layout titlePage="Home page">
|
||||
<div>
|
||||
<div className="uk-section">
|
||||
<div className="uk-container uk-container-large">
|
||||
@ -16,7 +19,8 @@ const Home = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home
|
||||
export default withApollo(Home)
|
||||
|
||||
@ -1,22 +1,148 @@
|
||||
import { ApolloClient } from 'apollo-client'
|
||||
import { InMemoryCache } from 'apollo-cache-inmemory'
|
||||
import withApollo from 'next-with-apollo'
|
||||
import { createHttpLink } from 'apollo-link-http'
|
||||
import React, { useMemo } from 'react'
|
||||
import Head from 'next/head'
|
||||
import { ApolloProvider } from '@apollo/react-hooks'
|
||||
import { ApolloClient, InMemoryCache, HttpLink } from 'apollo-boost'
|
||||
import fetch from 'isomorphic-unfetch'
|
||||
import NProgress from 'nprogress'
|
||||
import "../assets/css/style.css";
|
||||
|
||||
|
||||
import Router from 'next/router'
|
||||
Router.events.on('routeChangeStart', url => NProgress.start())
|
||||
Router.events.on('routeChangeComplete', () => NProgress.done())
|
||||
Router.events.on('routeChangeError', () => NProgress.done())
|
||||
|
||||
const GRAPHQL_URL = process.env.BACKEND_URL || 'http://localhost:1337/graphql'
|
||||
|
||||
const link = new createHttpLink({
|
||||
const link = new HttpLink({
|
||||
fetch,
|
||||
uri: GRAPHQL_URL
|
||||
})
|
||||
|
||||
export default withApollo(
|
||||
|
||||
({initialState}) =>
|
||||
new ApolloClient({
|
||||
link,
|
||||
cache: new InMemoryCache()
|
||||
.restore(initialState || {})
|
||||
|
||||
|
||||
let apolloClient = null
|
||||
|
||||
/**
|
||||
* Creates and provides the apolloContext
|
||||
* to a next.js PageTree. Use it by wrapping
|
||||
* your PageComponent via HOC pattern.
|
||||
* @param {Function|Class} PageComponent
|
||||
* @param {Object} [config]
|
||||
* @param {Boolean} [config.ssr=true]
|
||||
*/
|
||||
export function withApollo (PageComponent, { ssr = true } = {}) {
|
||||
const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
|
||||
const client = useMemo(
|
||||
() => apolloClient || initApolloClient(apolloState),
|
||||
[]
|
||||
)
|
||||
return (
|
||||
<ApolloProvider client={client}>
|
||||
<PageComponent {...pageProps} />
|
||||
</ApolloProvider>
|
||||
)
|
||||
}
|
||||
|
||||
// Set the correct displayName in development
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const displayName =
|
||||
PageComponent.displayName || PageComponent.name || 'Component'
|
||||
|
||||
if (displayName === 'App') {
|
||||
console.warn('This withApollo HOC only works with PageComponents.')
|
||||
}
|
||||
|
||||
WithApollo.displayName = `withApollo(${displayName})`
|
||||
}
|
||||
|
||||
// Allow Next.js to remove getInitialProps from the browser build
|
||||
if (typeof window === 'undefined') {
|
||||
if (ssr) {
|
||||
WithApollo.getInitialProps = async ctx => {
|
||||
const { AppTree } = ctx
|
||||
|
||||
let pageProps = {}
|
||||
if (PageComponent.getInitialProps) {
|
||||
pageProps = await PageComponent.getInitialProps(ctx)
|
||||
}
|
||||
|
||||
// Run all GraphQL queries in the component tree
|
||||
// and extract the resulting data
|
||||
const apolloClient = initApolloClient()
|
||||
|
||||
try {
|
||||
// Run all GraphQL queries
|
||||
await require('@apollo/react-ssr').getDataFromTree(
|
||||
<AppTree
|
||||
pageProps={{
|
||||
...pageProps,
|
||||
apolloClient
|
||||
}}
|
||||
/>
|
||||
)
|
||||
} catch (error) {
|
||||
// Prevent Apollo Client GraphQL errors from crashing SSR.
|
||||
// Handle them in components via the data.error prop:
|
||||
// https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error
|
||||
console.error('Error while running `getDataFromTree`', error)
|
||||
}
|
||||
|
||||
// getDataFromTree does not call componentWillUnmount
|
||||
// head side effect therefore need to be cleared manually
|
||||
Head.rewind()
|
||||
|
||||
// Extract query data from the Apollo store
|
||||
const apolloState = apolloClient.cache.extract()
|
||||
|
||||
return {
|
||||
...pageProps,
|
||||
apolloState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return WithApollo
|
||||
}
|
||||
|
||||
/**
|
||||
* Always creates a new apollo client on the server
|
||||
* Creates or reuses apollo client in the browser.
|
||||
* @param {Object} initialState
|
||||
*/
|
||||
function initApolloClient (initialState) {
|
||||
// Make sure to create a new client for every server-side request so that data
|
||||
// isn't shared between connections (which would be bad)
|
||||
if (typeof window === 'undefined') {
|
||||
return createApolloClient(initialState)
|
||||
}
|
||||
|
||||
// Reuse client on the client-side
|
||||
if (!apolloClient) {
|
||||
apolloClient = createApolloClient(initialState)
|
||||
}
|
||||
|
||||
return apolloClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and configures the ApolloClient
|
||||
* @param {Object} [initialState={}]
|
||||
*/
|
||||
function createApolloClient (initialState = {}) {
|
||||
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
|
||||
const isBrowser = typeof window !== 'undefined'
|
||||
return new ApolloClient({
|
||||
connectToDevTools: isBrowser,
|
||||
ssrMode: !isBrowser, // Disables forceFetch on the server (so queries are only run once)
|
||||
link: new HttpLink({
|
||||
uri: GRAPHQL_URL, // Server URL (must be absolute)
|
||||
credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
|
||||
// Use fetch() polyfill on the server
|
||||
fetch: !isBrowser && fetch
|
||||
}),
|
||||
cache: new InMemoryCache().restore(initialState)
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -5,7 +5,7 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"postinstall": "cd backend && npm install && cd ../frontend && npm install",
|
||||
"heroku-postbuild": "npm run --prefix ./frontend build",
|
||||
"build": "npm run --prefix ./frontend build",
|
||||
"start": "concurrently \"npm run --prefix ./backend/ start\" \"npm run --prefix ./frontend/ start\"",
|
||||
"download": "concurrently \"npm run --prefix ./backend/ install\" \"npm run --prefix ./frontend/ install\"",
|
||||
"dev": "concurrently \"yarn --cwd ./backend/ develop\" \"yarn --cwd ./frontend/ dev\""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user