First adoption of React hooks (#3096)

I did this as a warm-up to using [React hooks](https://reactjs.org/docs/hooks-intro.html), which provide a better way to accomplish stateful things and side effects using functional components. This allows all components to be written in the same style, improves testability, facilitates code reuse, etc.

There's [a intro here](https://reactjs.org/docs/hooks-intro.html) which links to [Dan's talk at React Conf](https://www.youtube.com/watch?time_continue=3599&v=dpw9EHDh2bM) which does a really good job of explaining why hooks are a good way to write components. He describes hooks as being the electrons and neutrons to components which are atoms. Low-level functionality which was always there in React, though not as accessibly or visibly.

This adds a lint rule that enforces "the rule of hooks" which says they have to be declared at the top level in the functional component.

I don't think this changeset does a fabulous job of showing off the improvements hooks allows, though I think it is still a good direction for this code.
This commit is contained in:
Paul Melnikow
2019-03-03 18:30:20 -05:00
committed by GitHub
parent 1a6b32b49a
commit c7deb5a0d1
6 changed files with 123 additions and 142 deletions

View File

@@ -5,12 +5,12 @@ const Donate = styled.div`
padding: 25px 50px;
`
const DonateBox = () => (
<Donate>
Love Shields? Please consider{' '}
<a href="https://opencollective.com/shields">donating</a> to sustain our
activities
</Donate>
)
export default DonateBox
export default function DonateBox() {
return (
<Donate>
Love Shields? Please consider{' '}
<a href="https://opencollective.com/shields">donating</a> to sustain our
activities
</Donate>
)
}

View File

@@ -1,14 +1,10 @@
import React from 'react'
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { dynamicBadgeUrl } from '../../core/badge-urls/make-badge-url'
import { InlineInput } from './common'
export default class DynamicBadgeMaker extends React.Component {
static propTypes = {
baseUrl: PropTypes.string,
}
state = {
function DynamicBadgeMaker({ baseUrl = document.location.href }) {
const [values, setValues] = useState({
datatype: '',
label: '',
dataUrl: '',
@@ -16,21 +12,25 @@ export default class DynamicBadgeMaker extends React.Component {
color: '',
prefix: '',
suffix: '',
})
const isValid = ['datatype', 'label', 'dataUrl', 'query'].every(
k => values[k]
)
const onChange = ({ target: { name, value } }) => {
setValues({
...values,
[name]: value,
})
}
makeBadgeUrl() {
const {
datatype,
label,
dataUrl,
query,
color,
prefix,
suffix,
} = this.state
const { baseUrl: baseUrl = document.location.href } = this.props
return dynamicBadgeUrl({
baseUrl: baseUrl || window.location.href,
const onSubmit = e => {
e.preventDefault()
const { datatype, label, dataUrl, query, color, prefix, suffix } = values
document.location = dynamicBadgeUrl({
baseUrl,
datatype,
label,
dataUrl,
@@ -41,69 +41,38 @@ export default class DynamicBadgeMaker extends React.Component {
})
}
handleSubmit(e) {
e.preventDefault()
document.location = this.makeBadgeUrl()
}
const inputs = [
{ name: 'label' },
{ name: 'dataUrl', placeholder: 'data url' },
{ name: 'query' },
{ name: 'color' },
{ name: 'prefix' },
{ name: 'suffix' },
]
get isValid() {
const { datatype, label, dataUrl, query } = this.state
return datatype && label && dataUrl && query
}
render() {
return (
<form onSubmit={e => this.handleSubmit(e)}>
<select
className="short"
onChange={event => this.setState({ datatype: event.target.value })}
value={this.state.datatype}
>
<option disabled value="">
data type
</option>
<option value="json">json</option>
<option value="xml">xml</option>
<option value="yaml">yaml</option>
</select>{' '}
return (
<form onSubmit={onSubmit}>
<select name="datatype" onChange={onChange} value={values.datatype}>
<option disabled value="">
data type
</option>
<option value="json">json</option>
<option value="xml">xml</option>
<option value="yaml">yaml</option>
</select>{' '}
{inputs.map(({ name, placeholder = name }) => (
<InlineInput
className="short"
onChange={event => this.setState({ label: event.target.value })}
placeholder="label"
value={this.state.label}
name={name}
onChange={onChange}
placeholder={placeholder}
value={values[name]}
/>
<InlineInput
className="short"
onChange={event => this.setState({ dataUrl: event.target.value })}
placeholder="data url"
value={this.state.dataUrl}
/>
<InlineInput
className="short"
onChange={event => this.setState({ query: event.target.value })}
placeholder="query"
value={this.state.query}
/>
<InlineInput
className="short"
onChange={event => this.setState({ color: event.target.value })}
placeholder="color"
value={this.state.color}
/>
<InlineInput
className="short"
onChange={event => this.setState({ prefix: event.target.value })}
placeholder="prefix"
value={this.state.prefix}
/>
<InlineInput
className="short"
onChange={event => this.setState({ suffix: event.target.value })}
placeholder="suffix"
value={this.state.suffix}
/>
<button disabled={!this.isValid}>Make Badge</button>
</form>
)
}
))}
<button disabled={!isValid}>Make Badge</button>
</form>
)
}
export default DynamicBadgeMaker
DynamicBadgeMaker.propTypes = {
baseUrl: PropTypes.string,
}

View File

@@ -1,65 +1,67 @@
import React from 'react'
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { staticBadgeUrl } from '../../core/badge-urls/make-badge-url'
import { InlineInput } from './common'
export default class StaticBadgeMaker extends React.Component {
static propTypes = {
baseUrl: PropTypes.string,
}
state = {
function StaticBadgeMaker({ baseUrl = document.location.href }) {
const [values, setValues] = useState({
label: '',
message: '',
color: '',
})
const isValid = ['message', 'color'].every(k => values[k])
const onChange = ({ target: { name, value } }) => {
setValues({
...values,
[name]: value,
})
}
handleSubmit(e) {
const onSubmit = e => {
e.preventDefault()
const { baseUrl } = this.props
const { label, message, color } = this.state
const badgeUrl = staticBadgeUrl({
baseUrl: baseUrl || window.location.href,
label,
message,
color,
})
document.location = badgeUrl
const { label, message, color } = values
document.location = staticBadgeUrl({ baseUrl, label, message, color })
}
render() {
return (
<form onSubmit={e => this.handleSubmit(e)}>
<InlineInput
onChange={event => this.setState({ label: event.target.value })}
placeholder="label"
value={this.state.label}
/>
<InlineInput
onChange={event => this.setState({ message: event.target.value })}
placeholder="message"
value={this.state.message}
/>
<InlineInput
list="default-colors"
onChange={event => this.setState({ color: event.target.value })}
placeholder="color"
value={this.state.color}
/>
<datalist id="default-colors">
<option value="brightgreen" />
<option value="green" />
<option value="yellowgreen" />
<option value="yellow" />
<option value="orange" />
<option value="red" />
<option value="lightgrey" />
<option value="blue" />
</datalist>
<button>Make Badge</button>
</form>
)
}
return (
<form onSubmit={onSubmit}>
<InlineInput
name="label"
onChange={onChange}
placeholder="label"
value={values.label}
/>
<InlineInput
name="message"
onChange={onChange}
placeholder="message"
value={values.message}
/>
<InlineInput
list="default-colors"
name="color"
onChange={onChange}
placeholder="color"
value={values.color}
/>
<datalist id="default-colors">
<option value="brightgreen" />
<option value="green" />
<option value="yellowgreen" />
<option value="yellow" />
<option value="orange" />
<option value="red" />
<option value="lightgrey" />
<option value="blue" />
</datalist>
<button disabled={!isValid}>Make Badge</button>
</form>
)
}
export default StaticBadgeMaker
StaticBadgeMaker.propTypes = {
baseUrl: PropTypes.string,
}