Created
November 16, 2017 20:33
-
-
Save mjackson/598be304a4c5e6495a0fc05807e4c377 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*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><Form/></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