Skip to content

Instantly share code, notes, and snippets.

@ecoreng
Created April 2, 2020 07:03
Show Gist options
  • Save ecoreng/d4507f63d468fdb883131764302fb1dc to your computer and use it in GitHub Desktop.
Save ecoreng/d4507f63d468fdb883131764302fb1dc to your computer and use it in GitHub Desktop.
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