Created
April 2, 2020 07:03
-
-
Save ecoreng/d4507f63d468fdb883131764302fb1dc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useEffect } from 'react' | |
import Layout from '../components/layout' | |
import withAuth from '../util/withAuth'; | |
import { Routes, Roles } from '../constants'; | |
import Sidebar from '../components/sidebar'; | |
import noop from 'lodash/noop'; | |
import { authHeader } from '../api/common'; | |
import deepmerge from 'deepmerge'; | |
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'; | |
// this goes in an api call definition file | |
const algoliaCall = ['https://hn.algolia.com/api/v1/search?query=dogs', { method: 'GET' }]; | |
// this goes in a common library | |
async function callJsonApi( | |
httpCallConfig = ['http://localhost'], | |
extraHttpConfig = {}, | |
setLoading = noop, | |
setError = ex => { if (ex) { throw ex } }, | |
bearerToken | |
) { | |
try { | |
const [endpoint, config = {}] = httpCallConfig; | |
const auth = bearerToken ? { headers: authHeader(bearerToken)} : {}; | |
setLoading(true); | |
setError(null); | |
const mergedConfig = deepmerge(config, extraHttpConfig, auth); | |
// deep merge uses a shallow copy of this and hence loses is implementation of the | |
// Signal interface or whatever it's called and doesn't work so we have to manually set it | |
// the same would happen to all objects that rely on their prototype | |
if ('signal' in extraHttpConfig) { | |
mergedConfig.signal = extraHttpConfig.signal | |
} | |
const response = await fetch(endpoint, mergedConfig); | |
const data = await response.json(); | |
setError(null); | |
return data; | |
} catch (ex) { | |
if (ex.name !== 'AbortError') { | |
setError(ex); | |
throw ex; | |
} | |
} finally { | |
setLoading(false); | |
} | |
} | |
// this is the React component | |
function Tipsets(props) { | |
const [loading, setLoading] = useState(false); | |
const [error, setError] = useState(null); | |
const [results, setResults] = useState(props.results); | |
useEffect(() => { | |
if (!props.loadedFromServer) { | |
const abortController = new AbortController(); | |
const signal = abortController.signal; | |
callJsonApi( | |
algoliaCall, | |
{ signal: signal }, | |
setLoading, | |
setError | |
).then(results => { | |
setResults(results.hits.slice(0, 3)) | |
}) | |
.catch(error => {}); | |
return () => abortController.abort(); | |
} | |
}, []); | |
return ( | |
<Layout title='Dashboard' sidebar={(<Sidebar/>)}> | |
Tipsets | |
{error ? error.message : ''} | |
{loading ? 'loading' : '.'} | |
{error ? ':(' : ':)'} | |
<div> | |
{results.map(result => ( | |
<h2 key={result.url}>{result.title}</h2> | |
))} | |
</div> | |
</Layout> | |
) | |
} | |
// Static method called by Next to load data on the server | |
Tipsets.getInitialProps = async ({ctx}) => { | |
const { isServer } = ctx; | |
let results = []; | |
if (isServer) { | |
const resultsAll = await callJsonApi( | |
algoliaCall | |
); | |
results = resultsAll.hits.slice(0, 3); | |
} | |
return { results, loadedFromServer: isServer }; | |
}; | |
// withAuth is a HOC that gates access to this "Next" component/page | |
export default withAuth( | |
Tipsets, | |
Roles.User, | |
Routes.login | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment