Skip to content

Instantly share code, notes, and snippets.

@mjackson
Created November 16, 2017 20:33
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 mjackson/598be304a4c5e6495a0fc05807e4c377 to your computer and use it in GitHub Desktop.
Save mjackson/598be304a4c5e6495a0fc05807e4c377 to your computer and use it in GitHub Desktop.
/*eslint-disable no-alert */
////////////////////////////////////////////////////////////////////////////////
// Exercise:
//
// Using context, implement the <Form>, <SubmitButton>, and <TextInput>
// components such that:
//
// - Clicking the <SubmitButton> calls <Form onSubmit>
// - Hitting "Enter" while in a <TextInput> submits the form
// - Don't use a <form> element, we're intentionally recreating the
// browser's built-in behavior
//
// Got extra time?
//
// - Send the values of all the <TextInput>s to the <Form onSubmit> handler
// without using DOM traversal APIs
// - Implement a <ResetButton> that resets the <TextInput>s in the form
////////////////////////////////////////////////////////////////////////////////
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
// in the parent:
// static childContextTypes
// getChildContext
// in the child:
// static contextTypes
// this.context
class Form extends React.Component {
static childContextTypes = {
form: PropTypes.shape({
submit: PropTypes.func.isRequired
}).isRequired
}
getChildContext() {
return {
form: {
submit: () => {
if (this.props.onSubmit) {
const values = this.getValues()
this.props.onSubmit(values)
}
},
addTextInput: (input) => {
this.textInputs.push(input)
},
removeTextInput: (input) => {
this.textInputs = this.textInputs.filter(item => item !== input)
}
}
}
}
textInputs = []
getValues() {
const values = this.textInputs.reduce((memo, input) => {
memo[input.props.name] = input.getValue()
return memo
}, {})
return values
}
render() {
return <div>{this.props.children}</div>
}
}
class SubmitButton extends React.Component {
static contextTypes = {
form: PropTypes.shape({
submit: PropTypes.func.isRequired
}).isRequired
}
render() {
return <button onClick={this.context.form.submit}>{this.props.children}</button>
}
}
class TextInput extends React.Component {
static contextTypes = {
form: PropTypes.shape({
submit: PropTypes.func.isRequired,
addTextInput: PropTypes.func.isRequired,
removeTextInput: PropTypes.func.isRequired
}).isRequired
}
componentDidMount() {
this.context.form.addTextInput(this)
}
componentWillUnmount() {
this.context.form.removeTextInput(this)
}
getValue() {
return this.node.value
}
render() {
return (
<input
type="text"
name={this.props.name}
placeholder={this.props.placeholder}
ref={node => this.node = node}
onKeyDown={event => {
if (event.key === 'Enter') {
this.context.form.submit()
}
}}
/>
)
}
}
class App extends React.Component {
handleSubmit = (values) => {
console.log(values)
}
state = {
showLastName: true
}
render() {
return (
<div>
<h1>This isn't even my final <code>&lt;Form/&gt;</code>!</h1>
<button
onClick={() => this.setState({ showLastName: false })}
>Hide the last name field</button>
<Form onSubmit={this.handleSubmit}>
<p>
<TextInput name="firstName" placeholder="First Name"/> {' '}
{this.state.showLastName && <TextInput name="lastName" placeholder="Last Name"/>}
</p>
<p>
<SubmitButton>Submit</SubmitButton>
</p>
</Form>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment