Created
September 7, 2017 23:22
-
-
Save mtomcal/dd0709ba308faeef70f1104903c73f60 to your computer and use it in GitHub Desktop.
Select Controlled Compound Component
This file contains hidden or 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
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