Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(gatsby-cli): convert hooks into class component #15903

Merged
merged 1 commit into from
Jul 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 60 additions & 63 deletions packages/gatsby-cli/src/reporter/reporters/ink/components/develop.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,7 @@
import React, { useContext, useState, useEffect, useRef } from "react"
import React, { Component } from "react"
import { Box, Color, StdoutContext } from "ink"
import fetch from "node-fetch"

// Handy hook from https://overreacted.io/making-setinterval-declarative-with-react-hooks/
function useInterval(callback, delay) {
const savedCallback = useRef()

// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback
}, [callback])

// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current()
}
if (delay !== null) {
let id = setInterval(tick, delay)
return () => clearInterval(id)
}

return null
}, [delay])
}

// Track the width and height of the terminal. Responsive app design baby!
const useTerminalResize = () => {
const { stdout } = useContext(StdoutContext)
const [sizes, setSizes] = useState([stdout.columns, stdout.rows])
useEffect(() => {
stdout.on(`resize`, () => {
setSizes([stdout.columns, stdout.rows])
})
return () => {
stdout.off(`resize`)
}
}, [stdout])

return sizes
}

// Query the site's graphql instance for the latest count.
const fetchPageQueryCount = url =>
fetch(`${url}___graphql`, {
Expand All @@ -57,33 +18,69 @@ const fetchPageQueryCount = url =>
.then(res => res.json())
.then(json => json.data.allSitePage.totalCount)

const Develop = props => {
const [pagesCount, setPagesCount] = useState(0)
class Develop extends Component {
state = {
pagesCount: 0,
sizes: [this.props.stdout.columns, this.props.stdout.rows],
}
timer = null

fetchPageQueryCount(props.stage.context.url).then(count =>
setPagesCount(count)
)
// Query for latest page count every second.
// Built-in subscriptions would be nice.
useInterval(() => {
// POST to get pages count.
fetchPageQueryCount(props.stage.context.url).then(count =>
setPagesCount(count)
fetchPageCount() {
fetchPageQueryCount(this.props.stage.context.url).then(pagesCount =>
this.setState({ pagesCount })
)
}, 1000)

const [width] = useTerminalResize()
this.timer = setTimeout(this.fetchPageCount.bind(this), 1000)
}

componentDidMount() {
this.fetchPageCount()

const { stdout } = this.props
stdout.on(`resize`, () => {
this.setState({
sizes: [stdout.columns, stdout.rows],
})
})
}

componentWillUpdate(nextProps) {
if (this.props.stdout !== nextProps.stdout) {
this.props.stdout.off(`resize`)

return (
<Box flexDirection="column" marginTop={2}>
<Box textWrap={`truncate`}>{`—`.repeat(width)}</Box>
<Box height={1} flexDirection="row">
<Color>{pagesCount} pages</Color>
<Box flexGrow={1} />
<Color>{props.stage.context.appName}</Color>
const { stdout } = nextProps
stdout.on(`resize`, () => {
this.setState({
sizes: [stdout.columns, stdout.rows],
})
})
}
}

componentWillUnmount() {
this.props.stdout.off(`resize`)

if (this.timer) {
clearTimeout(this.timer)
}
}

render() {
return (
<Box flexDirection="column" marginTop={2}>
<Box textWrap={`truncate`}>{`—`.repeat(this.state.sizes[0])}</Box>
<Box height={1} flexDirection="row">
<Color>{this.state.pagesCount} pages</Color>
<Box flexGrow={1} />
<Color>{this.props.stage.context.appName}</Color>
</Box>
</Box>
</Box>
)
)
}
}

export default Develop
export default props => (
<StdoutContext.Consumer>
{({ stdout }) => <Develop stdout={stdout} {...props} />}
</StdoutContext.Consumer>
)
34 changes: 33 additions & 1 deletion packages/gatsby-cli/src/reporter/reporters/ink/reporter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react"
import { Static, Box } from "ink"
import chalk from "chalk"
import { trackBuildError } from "gatsby-telemetry"
import Spinner from "./components/spinner"
import ProgressBar from "./components/progress-bar"
import Develop from "./components/develop"
Expand Down Expand Up @@ -126,8 +127,39 @@ export default class GatsbyReporter extends React.Component {
this._addMessage(`verbose`, str)
}

static getDerivedStateFromError(error) {
return { hasError: true, error: error.name }
}

componentDidCatch(error, info) {
trackBuildError(`INK`, {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jamo how do I track this error properly?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, this looks correct

error: {
message: error.name,
stack: info.componentStack,
},
})
}

render() {
const { activities, messages, disableColors, stage } = this.state
const {
activities,
messages,
disableColors,
stage,
hasError,
error,
} = this.state

if (hasError) {
// You can render any custom fallback UI
return (
<Box flexDirection="row">
<Message type="error" hideColors={disableColors}>
We've encountered an error: {error}
</Message>
</Box>
)
}

const spinners = []
const progressBars = []
Expand Down