Skip to content

Instantly share code, notes, and snippets.

@jeffwillette
Created July 9, 2018 10:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeffwillette/93be211a78b7f43a08b305e887c3ddd6 to your computer and use it in GitHub Desktop.
Save jeffwillette/93be211a78b7f43a08b305e887c3ddd6 to your computer and use it in GitHub Desktop.
import { ApolloClient } from 'apollo-client';
import { ApolloLink } from 'apollo-link';
import { ApolloProvider } from 'react-apollo';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { JssProvider } from 'react-jss';
import { renderToString } from 'react-dom/server';
import { withClientState } from 'apollo-link-state';
import React from 'react';
import { defaultState } from './src/state/defaults';
import { resolvers } from './src/state/local/resolvers';
import getPageContext from './src/getPageContext';
// this is for the usage of redux
// see https://github.com/mui-org/material-ui/blob/master/examples/gatsby/gatsby-ssr.js
// to see how the MUI SSR works
export const replaceRenderer = ({ bodyComponent, setHeadComponents, replaceBodyHTMLString }) => {
const client = createSSRClient();
const pageContext = getPageContext();
const bodyHTML = renderToString(
<JssProvider registry={pageContext.sheetsRegistry} generateClassName={pageContext.generateClassName}>
<ApolloProvider client={client}>{React.cloneElement(bodyComponent, { pageContext })}</ApolloProvider>
</JssProvider>
);
replaceBodyHTMLString(bodyHTML);
setHeadComponents([
<style
type="text/css"
id="server-side-jss"
key="server-side-jss"
dangerouslySetInnerHTML={{
__html: pageContext.sheetsRegistry.toString()
}}
/>
]);
};
const createSSRClient = () => {
const cache = new InMemoryCache();
const stateLink = withClientState({
cache,
resolvers,
defaultState
});
const client = new ApolloClient({
cache,
link: ApolloLink.from([stateLink]),
connectToDevTools: false
});
client.onResetStore(stateLink.writeDefaults);
return client;
};
import { createGenerateClassName, createMuiTheme, Theme } from '@material-ui/core';
import grey from '@material-ui/core/colors/grey';
import indigo from '@material-ui/core/colors/indigo';
import { SheetsRegistry } from 'react-jss';
export interface PageContext {
theme: any;
sheetsManager: Map<any, any>;
sheetsRegistry: any;
generateClassName: any;
}
interface CustomTheme extends Theme {
text: {
white: string;
};
}
const theme = createMuiTheme({
palette: {
primary: indigo,
secondary: grey,
error: {
light: 'rgba(128,0,0,.9)',
main: 'rgba(128,0,0,.9)',
dark: 'rgba(128,0,0,.9)',
contrastText: '#FFFFFF'
},
background: {
default: '#FFFFFF'
}
},
text: {
white: 'rgba(255,255,255,.5)'
},
overrides: {
MuiButton: {
root: {
borderRadius: 5
}
},
MuiPaper: {
root: {
boxShadow:
'0px 1px 1px -1px rgba(0, 0, 0, 0.2),' +
'0px 3px 1px -1px rgba(0, 0, 0, 0.14),' +
'0px 1px 1px -1px rgba(0, 0, 0, 0.12)'
},
rounded: {
borderRadius: 5
}
}
}
});
const createPageContext = (): PageContext => {
return {
theme,
// This is needed in order to deduplicate the injection of CSS in the page.
sheetsManager: new Map(),
// This is needed in order to inject the critical CSS.
sheetsRegistry: new SheetsRegistry(),
// The standard class name generator.
generateClassName: createGenerateClassName()
};
};
interface Process {
browser: any;
}
declare var process: Process;
interface Global {
__INIT_MATERIAL_UI__: any;
}
declare var global: Global;
const getPageContext = () => {
// Make sure to create a new context for every server-side request so that data
// isn't shared between connections (which would be bad).
if (!process.browser) {
return createPageContext();
}
// Reuse context on the client-side.
if (!global.__INIT_MATERIAL_UI__) {
global.__INIT_MATERIAL_UI__ = createPageContext();
}
return global.__INIT_MATERIAL_UI__;
};
export default getPageContext;
import 'typeface-roboto';
import './index.css';
import { Grid, withStyles } from '@material-ui/core';
import { IntlProvider, addLocaleData } from 'react-intl';
import { compose } from 'recompose';
import PropTypes from 'prop-types';
import React from 'react';
import en from 'react-intl/locale-data/en';
import zh from 'react-intl/locale-data/zh';
import { Viewer } from '../../state/remote/components';
import Notifications from '../Notifications/notifications';
import locales from '../../locales';
import withRoot from '../../withRoot';
addLocaleData([...en, ...zh]);
const styles = () => ({
pageContent: {
padding: 15
}
});
// this is the main entrypoint for the layout to the site
const GlobalLayout = ({ locale, children, classes }) => {
return (
<IntlProvider locale={locale} messages={locales[locale]}>
<Viewer>
{viewer => {
return (
<Grid container className={classes.pageContent}>
{/* the one of the children is a FullWidth or AppDrawer component, that
* component decides how to display the header and the drawer based on the
* device width. The following components after children have to be
* responsive to both
*/}
{children}
<Notifications />
</Grid>
);
}}
</Viewer>
</IntlProvider>
);
};
GlobalLayout.propTypes = {
children: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
locale: PropTypes.string,
classes: PropTypes.object
};
export default compose(
withRoot,
withStyles(styles)
)(GlobalLayout);
import CssBaseline from '@material-ui/core/CssBaseline';
import { MuiThemeProvider } from '@material-ui/core/styles';
import PropTypes from 'prop-types';
import React from 'react';
import getPageContext, { PageContext } from './getPageContext';
interface Props {
pageContext: PageContext;
}
const withRoot = (Component: React.SFC) => {
class WithRoot extends React.Component<Props, {}> {
public pageContext = {} as PageContext;
constructor(props: any, context: any) {
super(props, context);
this.pageContext = this.props.pageContext || getPageContext();
}
public componentDidMount() {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#server-side-jss');
if (jssStyles && jssStyles.parentNode) {
jssStyles.parentNode.removeChild(jssStyles);
}
}
public render() {
// MuiThemeProvider makes the theme available down the React tree thanks to React context.
return (
<MuiThemeProvider
theme={this.pageContext.theme}
sheetsManager={this.pageContext.sheetsManager}
>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<Component {...this.props} />
</MuiThemeProvider>
);
}
}
return WithRoot;
};
export default withRoot;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment