Skip to content

Instantly share code, notes, and snippets.

@mtomcal
Created September 7, 2017 23:22
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 mtomcal/dd0709ba308faeef70f1104903c73f60 to your computer and use it in GitHub Desktop.
Save mtomcal/dd0709ba308faeef70f1104903c73f60 to your computer and use it in GitHub Desktop.
Select Controlled Compound Component
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import './styles.css'
const { func, any } = PropTypes
////////////////////////////////////////////////////////////////////////////////
// Requirements
//
// Make this work like a normal <select><option/></select>
class Select extends React.Component {
static propTypes = {
onChange: func,
value: any,
defaultValue: any
}
static childContextTypes = {
select: PropTypes.shape({
onClick: PropTypes.func.isRequired
})
}
state = {
value: this.props.value ||
this.props.defaultValue ||
'Please select a value...',
isOpen: false
}
componentWillReceiveProps(nextProps) {
if (nextProps.value) {
this.setState({
value: nextProps.value
})
}
}
onOptionClick = (value) => () => {
this.setState({
value,
isOpen: false
}, () => this.props.onChange && this.props.onChange(this.state.value))
}
onClick = () => {
this.setState({
isOpen: !this.state.isOpen
})
}
getChildContext() {
return {
select: {
onClick: this.onOptionClick
}
}
}
render() {
const { value, isOpen } = this.state
return (
<div className="select">
<div className="label" onClick={this.onClick}>{value} <span className="arrow"></span></div>
{isOpen && <div className="options">
{this.props.children}
</div>
}
</div>
)
}
}
class Option extends React.Component {
static contextTypes = {
select: PropTypes.shape({
onClick: PropTypes.func.isRequired
}).isRequired
}
static propTypes = {
value: PropTypes.string.isRequired,
children: PropTypes.any
}
render() {
const { select: { onClick } } = this.context
const { value, children } = this.props
return (
<div className="option" onClick={onClick(value)}>{children}</div>
)
}
}
class App extends React.Component {
state = {
selectValue: 'dosa'
}
setToMintChutney = () => {
this.setState({ selectValue: 'mint-chutney' })
}
render() {
return (
<div>
<h1>Select/Option</h1>
<pre>{JSON.stringify(this.state, null, 2)}</pre>
<h2>Controlled</h2>
<p>
<button onClick={this.setToMintChutney}>Set to Mint Chutney</button>
</p>
<Select
value={this.state.selectValue}
onChange={(selectValue) => this.setState({ selectValue })}
>
<Option value="tikka-masala">Tikka Masala</Option>
<Option value="tandoori-chicken">Tandoori Chicken</Option>
<Option value="dosa">Dosa</Option>
<Option value="mint-chutney">Mint Chutney</Option>
</Select>
<h2>Uncontrolled</h2>
<Select defaultValue="tikka-masala">
<Option value="tikka-masala">Tikka Masala</Option>
<Option value="tandoori-chicken">Tandoori Chicken</Option>
<Option value="dosa">Dosa</Option>
<Option value="mint-chutney">Mint Chutney</Option>
</Select>
</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