Skip to content

Commit 0940cea

Browse files
committed
Refactor Layout to function component
1 parent 1d57fb7 commit 0940cea

File tree

3 files changed

+76
-88
lines changed

3 files changed

+76
-88
lines changed

gatsby-browser.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import Prism from "prism-react-renderer/prism"
1212

1313
require("prismjs/components/prism-solidity")
1414

15+
// Prevents <Layout/> from unmounting on page transitions
16+
// https://www.gatsbyjs.com/docs/layout-components/#how-to-prevent-layout-components-from-unmounting
1517
export const wrapPageElement = ({ element, props }) => {
1618
return <Layout {...props}>{element}</Layout>
1719
}

gatsby-ssr.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import React from "react"
88
import Layout from "./src/components/Layout"
99

10+
// Prevents <Layout/> from unmounting on page transitions
11+
// https://www.gatsbyjs.com/docs/layout-components/#how-to-prevent-layout-components-from-unmounting
1012
export const wrapPageElement = ({ element, props }) => {
1113
return <Layout {...props}>{element}</Layout>
1214
}

src/components/Layout.js

Lines changed: 72 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
// TODO figure out case, Netlify is case sensitive
2-
// https://community.netlify.com/t/support-guide-netlify-app-builds-locally-but-fails-on-deploy-case-sensitivity/10754
3-
import React from "react"
1+
import React, { useState, useEffect } from "react"
42
import { ThemeProvider } from "styled-components"
53
import { IntlProvider, IntlContextProvider } from "gatsby-plugin-intl"
64
import styled from "styled-components"
@@ -60,106 +58,92 @@ const StyledBannerNotification = styled(BannerNotification)`
6058
`
6159

6260
// TODO `Layout` renders twice on page load - why?
63-
// TODO refactor into function component
64-
class Layout extends React.Component {
65-
constructor(props) {
66-
super(props)
67-
this.state = {
68-
isDarkTheme: false,
69-
}
70-
}
61+
const Layout = (props) => {
62+
const [isDarkTheme, setIsDarkTheme] = useState(false)
7163

7264
// set isDarkTheme based on browser/app user preferences
73-
componentDidMount = () => {
65+
useEffect(() => {
7466
if (localStorage && localStorage.getItem("dark-theme") !== null) {
75-
this.setState({
76-
isDarkTheme: localStorage.getItem("dark-theme") === "true",
77-
})
67+
setIsDarkTheme(localStorage.getItem("dark-theme") === "true")
7868
} else {
79-
this.setState({
80-
isDarkTheme: window.matchMedia("(prefers-color-scheme: dark)").matches,
81-
})
69+
setIsDarkTheme(window.matchMedia("(prefers-color-scheme: dark)").matches)
8270
}
8371
window
8472
.matchMedia("(prefers-color-scheme: dark)")
8573
.addListener(({ matches }) => {
8674
if (localStorage && localStorage.getItem("dark-theme") === null) {
87-
this.setState({ isDarkTheme: matches })
88-
}
89-
})
90-
}
91-
92-
componentWillUnmount = () => {
93-
window
94-
.matchMedia("(prefers-color-scheme: dark)")
95-
.removeListener(({ matches }) => {
96-
if (localStorage && localStorage.getItem("dark-theme") === null) {
97-
this.setState({ isDarkTheme: matches })
75+
setIsDarkTheme(matches)
9876
}
9977
})
100-
}
78+
return () => {
79+
window
80+
.matchMedia("(prefers-color-scheme: dark)")
81+
.removeListener(({ matches }) => {
82+
if (localStorage && localStorage.getItem("dark-theme") === null) {
83+
setIsDarkTheme(matches)
84+
}
85+
})
86+
}
87+
}, [])
10188

102-
handleThemeChange = () => {
103-
const isDarkTheme = !this.state.isDarkTheme
104-
this.setState({ isDarkTheme: isDarkTheme })
89+
const handleThemeChange = () => {
90+
setIsDarkTheme(!isDarkTheme)
10591
if (localStorage) {
106-
localStorage.setItem("dark-theme", isDarkTheme)
92+
localStorage.setItem("dark-theme", !isDarkTheme)
10793
}
10894
}
10995

110-
render() {
111-
// IntlProvider & IntlContextProvider appear to be necessary in order to pass context
112-
// into components that live outside page components (e.g. Nav & Footer).
113-
// https://github.com/wiziple/gatsby-plugin-intl/issues/116
114-
const intl = this.props.pageContext.intl
115-
const theme = this.state.isDarkTheme ? darkTheme : lightTheme
116-
117-
const path = this.props.path
118-
const shouldShowSideNav = path.includes("/docs/")
119-
const shouldShowSubNav = path.includes("/developers/")
120-
const shouldShowBanner =
121-
path.includes("/eth2/") && !path.includes("/eth2/deposit-contract/")
122-
123-
return (
124-
<IntlProvider
125-
locale={intl.language}
126-
defaultLocale={intl.defaultLocale}
127-
messages={intl.messages}
128-
>
129-
<IntlContextProvider value={intl}>
130-
<ThemeProvider theme={theme}>
131-
<GlobalStyle isDarkTheme={this.state.isDarkTheme} />
132-
<ContentContainer>
133-
<Nav
134-
handleThemeChange={this.handleThemeChange}
135-
isDarkTheme={this.state.isDarkTheme}
136-
path={path}
137-
/>
138-
139-
<MainContainer
140-
shouldShowBanner={shouldShowBanner}
141-
shouldShowSubNav={shouldShowSubNav}
142-
shouldShowSideNav={shouldShowSideNav}
143-
>
144-
{shouldShowSideNav && <SideNav path={path} />}
145-
{shouldShowSideNav && <SideNavMobile path={path} />}
146-
<MainContent>
147-
<StyledBannerNotification shouldShow={shouldShowBanner}>
148-
Staking has arrived! If you're looking to stake your ETH,{" "}
149-
<Link to="/eth2/deposit-contract/">
150-
confirm the deposit contract address
151-
</Link>
152-
.
153-
</StyledBannerNotification>
154-
<Main>{this.props.children}</Main>
155-
</MainContent>
156-
</MainContainer>
157-
<Footer />
158-
</ContentContainer>
159-
</ThemeProvider>
160-
</IntlContextProvider>
161-
</IntlProvider>
162-
)
163-
}
96+
// IntlProvider & IntlContextProvider appear to be necessary in order to pass context
97+
// into components that live outside page components (e.g. Nav & Footer).
98+
// https://github.com/wiziple/gatsby-plugin-intl/issues/116
99+
const intl = props.pageContext.intl
100+
const theme = isDarkTheme ? darkTheme : lightTheme
101+
102+
const path = props.path
103+
const shouldShowSideNav = path.includes("/docs/")
104+
const shouldShowSubNav = path.includes("/developers/")
105+
const shouldShowBanner =
106+
path.includes("/eth2/") && !path.includes("/eth2/deposit-contract/")
107+
108+
return (
109+
<IntlProvider
110+
locale={intl.language}
111+
defaultLocale={intl.defaultLocale}
112+
messages={intl.messages}
113+
>
114+
<IntlContextProvider value={intl}>
115+
<ThemeProvider theme={theme}>
116+
<GlobalStyle isDarkTheme={isDarkTheme} />
117+
<ContentContainer>
118+
<Nav
119+
handleThemeChange={handleThemeChange}
120+
isDarkTheme={isDarkTheme}
121+
path={path}
122+
/>
123+
124+
<MainContainer
125+
shouldShowBanner={shouldShowBanner}
126+
shouldShowSubNav={shouldShowSubNav}
127+
shouldShowSideNav={shouldShowSideNav}
128+
>
129+
{shouldShowSideNav && <SideNav path={path} />}
130+
{shouldShowSideNav && <SideNavMobile path={path} />}
131+
<MainContent>
132+
<StyledBannerNotification shouldShow={shouldShowBanner}>
133+
Staking has arrived! If you're looking to stake your ETH,{" "}
134+
<Link to="/eth2/deposit-contract/">
135+
confirm the deposit contract address
136+
</Link>
137+
.
138+
</StyledBannerNotification>
139+
<Main>{props.children}</Main>
140+
</MainContent>
141+
</MainContainer>
142+
<Footer />
143+
</ContentContainer>
144+
</ThemeProvider>
145+
</IntlContextProvider>
146+
</IntlProvider>
147+
)
164148
}
165149
export default Layout

0 commit comments

Comments
 (0)