Skip to content

Commit ca8ccc9

Browse files
committed
Merge remote-tracking branch 'origin/master' into use-firebase-query
2 parents c14d451 + 17f95d5 commit ca8ccc9

File tree

6 files changed

+233
-200
lines changed

6 files changed

+233
-200
lines changed

gatsby-node.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ exports.createPages = ({ actions, graphql }) => {
3131
description
3232
}
3333
code
34+
tsCode
3435
}
3536
}
3637
}

src/components/PostTemplate.js

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import React, { useCallback, useMemo, useState } from "react";
2+
import { Link } from "gatsby";
3+
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
4+
import { tomorrow as codeStyle } from "react-syntax-highlighter/dist/esm/styles/prism";
5+
import analytics from "./../utils/analytics.js";
6+
import { Composes, Content, Hook, Info, Links, LinksLi, Name } from "./styled";
7+
8+
const PostTemplate = ({ content, frontmatter, slug, permalink }) => {
9+
const extraLinks = frontmatter.links || [];
10+
const [codeKey, setCodeKey] = useState("code");
11+
const isCodeSwitchAvailable = useMemo(() => Boolean(frontmatter.tsCode), []);
12+
13+
const handleSwitchCodeClick = useCallback(() => {
14+
setCodeKey(key => (key === "code" ? "tsCode" : "code"));
15+
}, []);
16+
17+
return (
18+
<Hook id={frontmatter.title}>
19+
<Name>
20+
<i className="fas fa-link link-icon" />
21+
<Link to={slug}>{frontmatter.title}</Link>
22+
</Name>
23+
24+
{frontmatter.composes && (
25+
<Composes>
26+
Composes:{" "}
27+
{frontmatter.composes.map((title, i) => (
28+
<>
29+
<Link to={`/${title}`}>{title}</Link>
30+
{i < frontmatter.composes.length - 1 ? "," : ""}{" "}
31+
</>
32+
))}
33+
</Composes>
34+
)}
35+
36+
<Content dangerouslySetInnerHTML={{ __html: content }} />
37+
{isCodeSwitchAvailable && (
38+
<Content>
39+
<button
40+
className="button is-secondary has-text-weight-semibold"
41+
onClick={handleSwitchCodeClick}
42+
>
43+
View in {codeKey === "code" ? "TypeScript" : "JavaScript"}
44+
</button>
45+
</Content>
46+
)}
47+
<SyntaxHighlighter
48+
language="javascript"
49+
style={codeStyle}
50+
customStyle={{
51+
borderRadius: "10px",
52+
padding: "1.5em",
53+
fontSize: "14px"
54+
}}
55+
>
56+
{frontmatter[codeKey]}
57+
</SyntaxHighlighter>
58+
59+
{(permalink === true || extraLinks.length > 0) && (
60+
<Links>
61+
<div className="links-title">
62+
<span role="img" aria-label="books">
63+
📚
64+
</span>
65+
Also check out:
66+
</div>
67+
<ul>
68+
{extraLinks.map((link, i) => (
69+
<LinksLi key={i}>
70+
<a
71+
target={link.target || "_blank"}
72+
href={link.url}
73+
onClick={() => {
74+
analytics.track("clickExtraLink");
75+
}}
76+
>
77+
{link.name}
78+
</a>{" "}
79+
-{" "}
80+
<span
81+
dangerouslySetInnerHTML={{
82+
__html: link.description
83+
}}
84+
/>
85+
</LinksLi>
86+
))}
87+
88+
{permalink === true && (
89+
<LinksLi key="divjoy">
90+
<a
91+
href="https://divjoy.com?utm_source=usehooks&utm_medium=website&utm_campaign=usehooks-post-links"
92+
onClick={() => {
93+
analytics.track("clickExtraDivjoyLink");
94+
}}
95+
>
96+
Divjoy
97+
</a>{" "}
98+
-{" "}
99+
<span>React starter kit from the creator of usehooks.com</span>
100+
</LinksLi>
101+
)}
102+
</ul>
103+
</Links>
104+
)}
105+
106+
<Info>
107+
<div className="level-item">{frontmatter.date}</div>
108+
<div className="level-item is-hidden-mobile">
109+
<span></span>
110+
</div>
111+
{frontmatter.sandbox && (
112+
<>
113+
<div className="level-item">
114+
<a target="_blank" href={frontmatter.sandbox}>
115+
Open in CodeSandbox
116+
</a>
117+
</div>
118+
<div className="level-item is-hidden-mobile">
119+
<span></span>
120+
</div>
121+
</>
122+
)}
123+
<div className="level-item">
124+
<a target="_blank" href={frontmatter.gist}>
125+
Suggest a change
126+
</a>
127+
</div>
128+
</Info>
129+
</Hook>
130+
);
131+
};
132+
133+
PostTemplate.displayName = "PostTemplate";
134+
135+
export default PostTemplate;

src/components/styled.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import styled from "styled-components";
2+
3+
export const Hook = styled.div`
4+
margin-bottom: 4rem;
5+
`;
6+
7+
export const Composes = styled.div.attrs({
8+
className: "subtitle"
9+
})`
10+
padding-top: 3px;
11+
font-size: 0.9rem !important;
12+
`;
13+
14+
export const Name = styled.h2.attrs({
15+
className: "title is-3"
16+
})`
17+
position: relative;
18+
19+
.link-icon {
20+
display: none;
21+
position: absolute;
22+
left: -30px;
23+
top: 10px;
24+
opacity: 0.3;
25+
font-size: 22px;
26+
}
27+
28+
a {
29+
color: inherit;
30+
}
31+
32+
&:hover {
33+
.link-icon {
34+
display: inline;
35+
}
36+
}
37+
`;
38+
39+
export const Content = styled.div`
40+
margin-bottom: 25px;
41+
`;
42+
43+
export const Links = styled.div`
44+
background-color: #f3f9f8;
45+
padding: 25px;
46+
margin-top: 15px;
47+
border-radius: 10px;
48+
49+
.links-title {
50+
margin-bottom: 8px;
51+
font-weight: bold;
52+
}
53+
`;
54+
55+
export const LinksLi = styled.li`
56+
margin-bottom: 5px;
57+
58+
&:last-of-type {
59+
margin-bottom: 0;
60+
}
61+
`;
62+
63+
export const Info = styled.div.attrs({
64+
className: "level"
65+
})`
66+
margin: 20px auto 0 auto;
67+
max-width: 560px;
68+
69+
span {
70+
padding: 0 0.5rem;
71+
opacity: 0.2;
72+
}
73+
`;
74+
75+
export const More = styled.div`
76+
text-align: center;
77+
font-size: 1.1rem;
78+
79+
.next {
80+
margin-left: 10px;
81+
font-weight: bold;
82+
}
83+
84+
i {
85+
opacity: 0.3;
86+
margin-right: 10px;
87+
}
88+
`;

src/pages/useAsync.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ links:
1616
description: React component and hook for declarative promise resolution and data fetching.
1717

1818
code: "import React, { useState, useEffect, useCallback } from 'react';\r\n\r\n\/\/ Usage\r\nfunction App() {\r\n const { execute, status, value, error } = useAsync(myFunction, false);\r\n\r\n return (\r\n <div>\r\n {status === 'idle' && <div>Start your journey by clicking a button<\/div>}\r\n {status === 'success' && <div>{value}<\/div>}\r\n {status === 'error' && <div>{error}<\/div>}\r\n <button onClick={execute} disabled={status === 'pending'}>\r\n {status !== 'pending' ? 'Click me' : 'Loading...'}\r\n <\/button>\r\n <\/div>\r\n );\r\n}\r\n\r\n\/\/ An async function for testing our hook.\r\n\/\/ Will be successful 50% of the time.\r\nconst myFunction = () => {\r\n return new Promise((resolve, reject) => {\r\n setTimeout(() => {\r\n const rnd = Math.random() * 10;\r\n rnd <= 5\r\n ? resolve('Submitted successfully \uD83D\uDE4C')\r\n : reject('Oh no there was an error \uD83D\uDE1E');\r\n }, 2000);\r\n });\r\n};\r\n\r\n\/\/ Hook\r\nconst useAsync = (asyncFunction, immediate = true) => {\r\n const [status, setStatus] = useState('idle');\r\n const [value, setValue] = useState(null);\r\n const [error, setError] = useState(null);\r\n\r\n \/\/ The execute function wraps asyncFunction and\r\n \/\/ handles setting state for pending, value, and error.\r\n \/\/ useCallback ensures the below useEffect is not called\r\n \/\/ on every render, but only if asyncFunction changes.\r\n const execute = useCallback(() => {\r\n setStatus('pending');\r\n setValue(null);\r\n setError(null);\r\n\r\n return asyncFunction()\r\n .then(response => {\r\n setValue(response);\r\n setStatus('success');\r\n })\r\n .catch(error => {\r\n setError(error);\r\n setStatus('error');\r\n });\r\n }, [asyncFunction]);\r\n\r\n \/\/ Call execute if we want to fire it right away.\r\n \/\/ Otherwise execute can be called later, such as\r\n \/\/ in an onClick handler.\r\n useEffect(() => {\r\n if (immediate) {\r\n execute();\r\n }\r\n }, [execute, immediate]);\r\n\r\n return { execute, status, value, error };\r\n};"
19+
tsCode: "import React, { useState, useEffect, useCallback } from 'react';\r\n\r\n// Usage\r\nfunction App() {\r\n const { execute, status, value, error } = useAsync<string>(myFunction, false);\r\n\r\n return (\r\n <div>\r\n {status === 'idle' && <div>Start your journey by clicking a button</div>}\r\n {status === 'success' && <div>{value}</div>}\r\n {status === 'error' && <div>{error}</div>}\r\n <button onClick={execute} disabled={status === 'pending'}>\r\n {status !== 'pending' ? 'Click me' : 'Loading...'}\r\n </button>\r\n </div>\r\n );\r\n}\r\n\r\n// An async function for testing our hook.\r\n// Will be successful 50% of the time.\r\nconst myFunction = (): Promise<string> => {\r\n return new Promise((resolve, reject) => {\r\n setTimeout(() => {\r\n const rnd = Math.random() * 10;\r\n rnd <= 5\r\n ? resolve('Submitted successfully 🙌')\r\n : reject('Oh no there was an error 😞');\r\n }, 2000);\r\n });\r\n};\r\n\r\n// Hook\r\nconst useAsync = <T, E = string>(\r\n asyncFunction: () => Promise<T>,\r\n immediate = true\r\n) => {\r\n const [status, setStatus] = useState<\r\n 'idle' | 'pending' | 'success' | 'error'\r\n >('idle');\r\n const [value, setValue] = useState<T | null>(null);\r\n const [error, setError] = useState<E | null>(null);\r\n\r\n // The execute function wraps asyncFunction and\r\n // handles setting state for pending, value, and error.\r\n // useCallback ensures the below useEffect is not called\r\n // on every render, but only if asyncFunction changes.\r\n const execute = useCallback(() => {\r\n setStatus('pending');\r\n setValue(null);\r\n setError(null);\r\n\r\n return asyncFunction()\r\n .then((response: any) => {\r\n setValue(response);\r\n setStatus('success');\r\n })\r\n .catch((error: any) => {\r\n setError(error);\r\n setStatus('error');\r\n });\r\n }, [asyncFunction]);\r\n\r\n // Call execute if we want to fire it right away.\r\n // Otherwise execute can be called later, such as\r\n // in an onClick handler.\r\n useEffect(() => {\r\n if (immediate) {\r\n execute();\r\n }\r\n }, [execute, immediate]);\r\n\r\n return { execute, status, value, error };\r\n};"
1920
---
2021

2122
It's generally a good practice to indicate to users the status of any async request. An example would be fetching data from an API and displaying a loading indicator before rendering the results. Another example would be a form where you want to disable the submit button when the submission is pending and then display either a success or error message when it completes.

src/templates/index.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import React from "react";
2-
import styled from "styled-components";
32
import { Link } from "gatsby";
43
import Layout from "../components/Layout";
5-
import Search from "../components/Search";
6-
import { PostTemplate } from "../templates/post.js";
4+
// import Search from "../components/Search";
5+
import PostTemplate from "../components/PostTemplate";
76

87
const IndexPage = ({ pageContext }) => {
98
//const [search, setSearch] = useState("");

0 commit comments

Comments
 (0)