@render-props/value
Version:
A state container which provides an interface for setting single values.
103 lines (83 loc) • 2.43 kB
JavaScript
import React from 'react'
import PropTypes from 'prop-types'
import {callIfExists} from '@render-props/utils'
/**
import Value from '@render-props/value'
function ValueSetter () {
return (
<Value initialValue='foo'>
{({setValue, clearValue, resetValue, value}) => (
<>
<button onClick={() => setValue(value === 'foo' ? 'bar' : 'foo')}>
Change value to '{value === 'foo' ? 'bar' : 'foo'}'
</button>
<button onClick={clearValue}>
Make value 'undefined'
</button>
<button onClick={resetValue}>
Reset value to initialValue
</button>
</>
)}
</Value>
)
}
*/
function maybeSetValue (value) {
return function (prevState, props) {
if (props.value === void 0) {
let nextValue = value
if (typeof value === 'function') {
nextValue = value(prevState.value, props)
}
if (prevState.value === nextValue) {
return null
}
return {value: nextValue}
}
if (__DEV__) {
throw new Error(
`[Value] You called 'setValue', 'resetValue' or 'clearValue' on a ` +
`controlled component. Did you mean to set 'initialValue' instead of ` +
`'value'?`
)
}
return null
}
}
export default class Value extends React.Component {
static propTypes = {
children: PropTypes.func.isRequired,
initialValue: PropTypes.any,
value: PropTypes.any,
onChange: PropTypes.func,
}
constructor (props) {
super(props)
this.state = {value: props.value === void 0 ? props.initialValue : props.value}
this.valueContext = {
setValue: this.setValue,
resetValue: this.resetValue,
clearValue: this.clearValue
}
}
componentDidUpdate (_, {value}) {
if (value !== this.state.value) {
callIfExists(this.props.onChange, this.state.value)
}
if (
this.props.value !== void 0
&& this.props.value !== this.state.value
) {
this.setState({value: this.props.value})
}
}
setValue = value => this.setState(maybeSetValue(value))
resetValue = () => this.setState(maybeSetValue(this.props.initialValue))
clearValue = () => this.setState(maybeSetValue(void 0))
render () {
this.valueContext.value = this.state.value
/** value, setValue, resetValue, clearValue */
return this.props.children(this.valueContext)
}
}