Skip to content

Commit

Permalink
Remix
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanflorence committed Dec 8, 2021
1 parent 8bb054d commit 23a3b46
Show file tree
Hide file tree
Showing 44 changed files with 8,307 additions and 2,124 deletions.
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
node_modules
.next
*.log

.cache
.vercel
.output

public/build
api/build
34 changes: 23 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
# Next.js 12 React Server Components Demo (Alpha)
# Welcome to Remix!

This is the demo of Hacker News built with Next.js and React Server Components. Read our announcement here: [Next.js 12](https://nextjs.org/blog/next-12).
- [Remix Docs](https://remix.run/docs)

**Try the demo: https://next-news-rsc.vercel.sh**
## Deployment

### Development
After having run the `create-remix` command and selected "Vercel" as a deployment target, you only need to [import your Git repository](https://vercel.com/new) into Vercel, and it will be deployed.

To get started, run the following commands:
If you'd like to avoid using a Git repository, you can also deploy the directory by running [Vercel CLI](https://vercel.com/cli):

```sh
npm i -g vercel
vercel
```
yarn
yarn dev

It is generally recommended to use a Git repository, because future commits will then automatically be deployed by Vercel, through its [Git Integration](https://vercel.com/docs/concepts/git).

## Development

To run your Remix app locally, make sure your project's local dependencies are installed:

```sh
npm install
```

And visit localhost:3000.
Afterwards, start the Remix development server like so:

### Note
```sh
npm run dev
```

React Server Components are still [experimental](https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html). To learn more about React Server Components, read our blog post: [Everything About React Server Components](https://vercel.com/blog/everything-about-react-server-components).
Open up [http://localhost:3000](http://localhost:3000) and you should be ready to go!

React Server Components support is a built-in feature of Next.js 12. Full documentation is available here: [React 18 — Next.js](https://nextjs.org/docs/advanced-features/react-18).
If you're used to using the `vercel dev` command provided by [Vercel CLI](https://vercel.com/cli) instead, you can also use that, but it's not needed.
5 changes: 5 additions & 0 deletions api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const { createRequestHandler } = require("@remix-run/vercel");

module.exports = createRequestHandler({
build: require("./build")
});
File renamed without changes.
25 changes: 12 additions & 13 deletions components/header.js → app/components/header.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Nav from './nav'
import Logo from './logo'
import Link from 'next/link'
import Nav from "./nav";
import Logo from "./logo";

export default function Header() {
return (
Expand Down Expand Up @@ -65,22 +64,22 @@ export default function Header() {
/>
<header>
<div className="left">
<Link href="/">
<a>
<span className="logo">
<Logo />
</span>
<span className="site-title">Hacker Next</span>
</a>
</Link>
<a href="/">
<span className="logo">
<Logo />
</span>
<span className="site-title">Hacker Next</span>
</a>
<div className="nav">
<Nav />
</div>
</div>
<div className="right">
<a href="/login" className="login">login</a>
<a href="/login" className="login">
login
</a>
</div>
</header>
</>
)
);
}
8 changes: 4 additions & 4 deletions components/item.client.js → app/components/item.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Story from '../components/story.client'
import Comment from '../components/comment'
import CommentForm from '../components/comment-form'
import Story from "./story";
import Comment from "./comment";
import CommentForm from "./comment-form";

export default function Item({ story, comments = null }) {
return (
Expand Down Expand Up @@ -39,5 +39,5 @@ export default function Item({ story, comments = null }) {
}
`}</style>
</div>
)
);
}
44 changes: 44 additions & 0 deletions app/components/item.sc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { Suspense } from "react";
import { useRouter } from "next/router";

import Page from "./page";
import Item from "./item";

import getItem from "../lib/get-item";
import getComments from "../app/lib/get-comments";

let commentsData = {};
let storyData = {};
let fetchDataPromise = {};

function ItemPageWithData({ id }) {
if (!commentsData[id]) {
if (!fetchDataPromise[id]) {
fetchDataPromise[id] = getItem(id)
.then((story) => {
storyData[id] = story;
return getComments(story.comments);
})
.then((c) => (commentsData[id] = c));
}
throw fetchDataPromise[id];
}

return (
<Page>
<Item story={storyData[id]} comments={commentsData[id]} />
</Page>
);
}

export default function ItemPage() {
const { id } = useRouter().query;

if (!id) return null;

return (
<Suspense>
<ItemPageWithData id={id} />
</Suspense>
);
}
File renamed without changes.
14 changes: 4 additions & 10 deletions components/meta.js → app/components/meta.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import Head from "next/head";
// import Router from 'next/router'

export default function Meta() {
return (
<div>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charSet="utf-8" />
{/* <link rel="shortcut icon" href="/static/favicon.ico" /> */}
</Head>
<>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charSet="utf-8" />
<style
dangerouslySetInnerHTML={{
__html: `
Expand Down Expand Up @@ -88,6 +82,6 @@ export default function Meta() {
`,
}}
/>
</div>
</>
);
}
28 changes: 15 additions & 13 deletions components/nav.js → app/components/nav.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
export default function Nav() {
;<ul className="nav-ul">
<Item href="/newest">new</Item>
<Item href="/show">show</Item>
<Item href="/ask">ask</Item>
<Item href="/jobs">jobs</Item>
<Item href="/submit">submit</Item>
return (
<ul className="nav-ul">
<Item href="/newest">new</Item>
<Item href="/show">show</Item>
<Item href="/ask">ask</Item>
<Item href="/jobs">jobs</Item>
<Item href="/submit">submit</Item>

<style
dangerouslySetInnerHTML={{
__html: `
<style
dangerouslySetInnerHTML={{
__html: `
.nav-ul {
list-style-type: none;
}
Expand All @@ -27,13 +28,14 @@ export default function Nav() {
color: #fff;
}
`,
}}
/>
</ul>
}}
/>
</ul>
);
}

const Item = ({ href, children }) => (
<li>
<a href={href}>{children}</a>
</li>
)
);
7 changes: 3 additions & 4 deletions components/page.client.js → app/components/page.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Header from './header'
import Meta from './meta'
import Header from "./header";
import Meta from "./meta";

export default function Page({ children }) {
return (
Expand Down Expand Up @@ -27,10 +27,9 @@ export default function Page({ children }) {
}}
/>
<div className="main">
<Meta />
<Header />
<div className="page">{children}</div>
</div>
</>
)
);
}
File renamed without changes.
File renamed without changes.
12 changes: 6 additions & 6 deletions components/stories.client.js → app/components/stories.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Story from './story.client'
import Link from 'next/link'
import Story from "./story";
import { Link } from "remix";

export default ({ stories, page = 1, offset = null }) => (
<div>
Expand All @@ -14,8 +14,8 @@ export default ({ stories, page = 1, offset = null }) => (
</div>
))}
<footer className="footer">
<Link prefetch href={`/news?p=${page + 1}`}>
<a>More</a>
<Link href={`/news?p=${page + 1}`}>
More
</Link>
</footer>

Expand All @@ -35,7 +35,7 @@ export default ({ stories, page = 1, offset = null }) => (
text-align: right;
}
.count::after {
content: '.';
content: ".";
}
.story {
flex: 100;
Expand All @@ -52,4 +52,4 @@ export default ({ stories, page = 1, offset = null }) => (
}
`}</style>
</div>
)
);
53 changes: 53 additions & 0 deletions app/components/story.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useState } from "react";

import timeAgo from "../lib/time-ago";

export default function Story({
id,
title,
date,
url,
user,
score,
commentsCount,
}) {
const { host } = url ? new URL(url) : { host: "#" };
const [voted, setVoted] = useState(false);

return (
<div style={{ margin: "5px 0" }}>
<div className="title">
<span
style={{
cursor: "pointer",
fontFamily: "sans-serif",
marginRight: 5,
color: voted ? "#ffa52a" : "#ccc",
}}
onClick={() => setVoted(!voted)}
>
&#9650;
</span>
<a href={url}>{title}</a>
{url && (
<span className="source">
<a href={`http://${host}`}>{host.replace(/^www\./, "")}</a>
</span>
)}
</div>
<div className="meta">
{score} {plural(score, "point")} by{" "}
<a href={`/user?id=${user}`}>{user}</a>{" "}
<a href={`/item?id=${id}`}>
{timeAgo(new Date(date)) /* note: we re-hydrate due to ssr */} ago
</a>{" "}
|{" "}
<a href={`/item?id=${id}`}>
{commentsCount} {plural(commentsCount, "comment")}
</a>
</div>
</div>
);
}

const plural = (n, s) => s + (n === 0 || n > 1 ? "s" : "");
4 changes: 4 additions & 0 deletions app/entry.client.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";

hydrate(<RemixBrowser />, document);
21 changes: 21 additions & 0 deletions app/entry.server.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { renderToString } from "react-dom/server";
import { RemixServer } from "remix";

export default function handleRequest(
request,
responseStatusCode,
responseHeaders,
remixContext
) {
let markup = renderToString(
<RemixServer context={remixContext} url={request.url} />
);

responseHeaders.set("Content-Type", "text/html");
responseHeaders.set("Cache-Control", "s-maxage=5, stale-while-revalidate=5");

return new Response("<!DOCTYPE html>" + markup, {
status: responseStatusCode,
headers: responseHeaders,
});
}
8 changes: 4 additions & 4 deletions lib/fetch-data.js → app/lib/fetch-data.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export default async function fetchData(type, delay = 0) {
const [res] = await Promise.all([
fetch(`https://hacker-news.firebaseio.com/v0/${type}.json`),
new Promise(res => setTimeout(res, (Math.random()) * delay))
])
new Promise((res) => setTimeout(res, Math.random() * delay)),
]);
if (res.status !== 200) {
throw new Error(`Status ${res.status}`)
throw new Error(`Status ${res.status}`);
}
return res.json()
return res.json();
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions pages/csr.js → app/pages/csr.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { Suspense } from "react";
import Spinner from "../components/spinner";

// Client Components
import Page from "../components/page.client";
import Story from "../components/story.client";
import Page from "../components/page";
import Story from "../components/story";

// Utils
import fetchData from "../lib/fetch-data";
Expand Down
File renamed without changes.
Loading

0 comments on commit 23a3b46

Please sign in to comment.