From 64f4c4840bac3262710def805ad609de04650f8c Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Sat, 15 Feb 2020 10:20:57 +0900 Subject: [PATCH] Move API calls on community page to client side (#995) * Move API calls on community page to client side * Replace API functions with hooks * Update src/components/Community/Learn/index.js Co-Authored-By: Jorge Orpinel * Update src/components/Community/Meet/index.js Co-Authored-By: Jorge Orpinel * Update src/components/Community/Meet/index.js Co-Authored-By: Jorge Orpinel * Update src/utils/api.js Co-Authored-By: Jorge Orpinel * Restyled by prettier (#999) Co-authored-by: Ivan Shcheklein Co-authored-by: Jorge Orpinel Co-authored-by: restyled-io[bot] <32688539+restyled-io[bot]@users.noreply.github.com> --- pages/community.js | 22 +------ src/components/Community/Learn/index.js | 34 ++++------ src/components/Community/Meet/index.js | 36 +++++----- src/components/Community/index.js | 13 +--- src/utils/api.js | 87 +++++++++++++++---------- 5 files changed, 89 insertions(+), 103 deletions(-) diff --git a/pages/community.js b/pages/community.js index c4a2a88d98..2553720e5a 100644 --- a/pages/community.js +++ b/pages/community.js @@ -1,17 +1,11 @@ import React from 'react' import Head from 'next/head' -import { - getLatestIssues, - getLatestTopics, - getLatestPosts -} from '../src/utils/api' - import Community from '../src/components/Community' import { META_BASE_TITLE } from '../src/consts' -export default function CommunityPage(props) { +export default function CommunityPage() { return ( <> @@ -28,19 +22,7 @@ export default function CommunityPage(props) { /> Community | {META_BASE_TITLE} - + ) } - -CommunityPage.getInitialProps = async ({ req }) => { - const issues = await getLatestIssues(req) - const posts = await getLatestPosts(req) - const topics = await getLatestTopics(req) - - return { - issues, - posts, - topics - } -} diff --git a/src/components/Community/Learn/index.js b/src/components/Community/Learn/index.js index 0b34b74060..39862c2bcd 100644 --- a/src/components/Community/Learn/index.js +++ b/src/components/Community/Learn/index.js @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react' +import React, { useCallback } from 'react' import PropTypes from 'prop-types' import format from 'date-fns/format' @@ -10,6 +10,7 @@ import CommunityBlock from '../Block' import CommunityButton from '../Button' import CommunitySection from '../Section' +import { usePosts, useCommentsCount } from '../../../utils/api' import { pluralizeComments } from '../../../utils/i18n' import { @@ -45,19 +46,10 @@ function CommunityBlogPost({ commentsUrl, pictureUrl }) { - const [count, setCount] = useState() - const loaded = count !== undefined const logPost = useCallback(() => logEvent('community', 'blog', title), [ title ]) - - useEffect(() => { - if (commentsUrl) { - fetch(`/api/comments?url=${commentsUrl}`) - .then(result => result.json()) - .then(data => setCount(data.count)) - } - }, []) + const { error, ready, result } = useCommentsCount(commentsUrl) return ( @@ -82,14 +74,14 @@ function CommunityBlogPost({ {title} - {loaded && ( + {ready && !error && ( <> - {pluralizeComments(count)} + {pluralizeComments(result)} {' • '} @@ -185,7 +177,9 @@ CommunityDocumentation.propTypes = { url: PropTypes.string } -export default function CommunityLearn({ posts, theme }) { +export default function CommunityLearn({ theme }) { + const { error, ready, result: posts } = usePosts() + return ( } action={ - posts.length && ( + posts && ( - {posts.length ? ( + {!ready && Loading...} + {error && Blog unavailable right now} + {posts && posts.map(post => ( - )) - ) : ( - Blog is unavailable right now - )} + ))} @@ -286,7 +279,6 @@ export default function CommunityLearn({ posts, theme }) { } CommunityLearn.propTypes = { - posts: PropTypes.array, theme: PropTypes.shape({ backgroundColor: PropTypes.string, color: PropTypes.string diff --git a/src/components/Community/Meet/index.js b/src/components/Community/Meet/index.js index bcddb9c897..8b682a94af 100644 --- a/src/components/Community/Meet/index.js +++ b/src/components/Community/Meet/index.js @@ -8,6 +8,7 @@ import CommunitySection from '../Section' import { pluralizeComments } from '../../../utils/i18n' import { logEvent } from '../../../utils/ga' +import { useIssues, useTopics } from '../../../utils/api' import data from '../data' @@ -110,7 +111,10 @@ CommunityIssue.propTypes = { color: PropTypes.string } -export default function CommunityMeet({ issues, theme, topics }) { +export default function CommunityMeet({ theme }) { + const { erorr: issuesError, ready: issuesReady, result: issues } = useIssues() + const { erorr: topicsError, ready: topicsReady, result: topics } = useTopics() + return ( } action={ - topics.length && ( + topics && ( - {topics.length ? ( + {!topicsReady && Loading...} + {topicsError && ( + Forum unavailable right now + )} + {topics && topics.map(topic => ( - )) - ) : ( - Forum is unavailable right now - )} + ))} @@ -217,7 +222,7 @@ export default function CommunityMeet({ issues, theme, topics }) { } action={ - issues.length && ( + issues && ( - {issues.length ? ( + {!issuesReady && Loading...} + {issuesError && ( + Github unavailable right now + )} + {issues && issues.map(issue => ( - )) - ) : ( - Github is unavailable right now - )} + ))} @@ -251,10 +257,8 @@ export default function CommunityMeet({ issues, theme, topics }) { } CommunityMeet.propTypes = { - issues: PropTypes.array, theme: PropTypes.shape({ backgroundColor: PropTypes.string, color: PropTypes.string - }), - topics: PropTypes.array + }) } diff --git a/src/components/Community/index.js b/src/components/Community/index.js index b73c2be15a..07b2b61fdb 100644 --- a/src/components/Community/index.js +++ b/src/components/Community/index.js @@ -1,5 +1,4 @@ import React from 'react' -import PropTypes from 'prop-types' import Page from '../Page' import Subscribe from '../Subscribe' @@ -18,23 +17,17 @@ const themes = { purple: { backgroundColor: '#DCD6F1', color: '#955DD6' } } -export default function Community({ issues, posts, topics }) { +export default function Community() { return ( - + - + ) } - -Community.propTypes = { - issues: PropTypes.array, - posts: PropTypes.array, - topics: PropTypes.array -} diff --git a/src/utils/api.js b/src/utils/api.js index 773209d351..0e77d2ddeb 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -1,3 +1,4 @@ +import { useEffect, useState } from 'react' import fetch from 'isomorphic-fetch' export function makeAbsoluteURL(req, uri) { @@ -7,50 +8,64 @@ export function makeAbsoluteURL(req, uri) { return `${protocol}//${host}${uri}` } -export async function getLatestIssues(req) { - try { - const res = await fetch(makeAbsoluteURL(req, '/api/github')) - - if (res.status !== 200) return [] - - const { issues } = await res.json() - - return issues - } catch (e) { - console.error(e) - - return [] - } +const useAPICall = url => { + const [ready, setReady] = useState(false) + const [error, setError] = useState(false) + const [result, setResult] = useState(null) + + useEffect(() => { + let cancelled = false + + const fetchData = async () => { + try { + const res = await fetch(url) + + if (!cancelled) { + if (res.status !== 200) { + setError('Bad response status') + } else { + setResult(await res.json()) + } + } + } catch { + if (!cancelled) setError('Error loading request') + } finally { + if (!cancelled) setReady(true) + } + } + + fetchData() + + return () => { + cancelled = true + } + }, []) + + return { error, ready, result } } -export async function getLatestPosts(req) { - try { - const res = await fetch(makeAbsoluteURL(req, '/api/blog')) +export function useIssues() { + const { error, ready, result } = useAPICall('/api/github') - if (res.status !== 200) return [] - - const { posts } = await res.json() + return { error, ready, result: result && result.issues } +} - return posts - } catch (e) { - console.error(e) +export function usePosts() { + const { error, ready, result } = useAPICall('/api/blog') - return [] - } + return { error, ready, result: result && result.posts } } -export async function getLatestTopics(req) { - try { - const res = await fetch(makeAbsoluteURL(req, '/api/discourse')) +export function useTopics() { + const { error, ready, result } = useAPICall('/api/discourse') - if (res.status !== 200) return [] - - const { topics } = await res.json() + return { error, ready, result: result && result.topics } +} - return topics - } catch (e) { - console.error(e) +export function useCommentsCount(commentsUrl) { + const { error, ready, result } = useAPICall( + `/api/comments?url=${commentsUrl}` + ) - return [] - } + return { error, ready, result: result && result.count } }