Created
September 30, 2019 03:46
-
-
Save mjasnikovs/77a6a48323d14301865d8d94076b335e to your computer and use it in GitHub Desktop.
Input Guard for Apollo client local state management
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
import React, {Component} from 'react' | |
import PropTypes from 'prop-types' | |
const MAX_INT = 2147483647 | |
const MIN_INT = -2147483648 | |
const isNaturalNumber = input => { | |
return typeof input === 'number' && input % 1 === 0 && Number.isInteger(input) && input >= 0 && 1 / input !== -Infinity | |
} | |
const isInteger = input => { | |
if (!Number.isInteger(input)) { | |
return false | |
} | |
if (typeof input !== 'number' || input !== Math.floor(input) || input === Infinity || isNaN(input)) { | |
return false | |
} | |
if (input > MAX_INT || input < MIN_INT) { | |
return false | |
} | |
return true | |
} | |
const isFloat = input => { | |
if (typeof input !== 'number' || input !== parseFloat(input) || input === Infinity || isNaN(input)) { | |
return false | |
} | |
if (input > MAX_INT || input < MIN_INT) { | |
return false | |
} | |
return true | |
} | |
class InputGuard extends Component { | |
constructor (props) { | |
super(props) | |
this.state = { | |
currentValue: props.children.props.value, | |
isFocused: false | |
} | |
this.handleChange = this.handleChange.bind(this, props.children.props.onChange) | |
this.handleFocus = this.handleFocus.bind(this, props.children.props.onFocus) | |
this.handleBlur = this.handleBlur.bind(this, props.children.props.onBlur) | |
} | |
handleChange (onChange, e) { | |
if (this.props.type === 'string' || e.target.value === '') { | |
this.setState({currentValue: e.target.value}) | |
if (typeof onChange === 'function') { | |
return onChange(e) | |
} | |
return null | |
} else if (this.props.type === 'int' && !isInteger(Number(e.target.value))) { | |
return null | |
} else if (this.props.type === 'float' && !isFloat(Number(e.target.value))) { | |
return null | |
} else if (this.props.type === 'naturalNumber' && !isNaturalNumber(Number(e.target.value))) { | |
return null | |
} else if (this.props.notZero === true && Number(e.target.value) === 0) { | |
return null | |
} | |
const event = {...e, ...{target: {...e.target, value: Number(e.target.value)}}} | |
this.setState({currentValue: event.target.value}) | |
if (typeof onChange === 'function') { | |
return onChange(event) | |
} | |
} | |
handleFocus (onFocus, e) { | |
this.setState({isFocused: true}) | |
if (typeof onFocus === 'function') { | |
onFocus(e) | |
} | |
} | |
handleBlur (onBlur, e) { | |
this.setState({isFocused: false}) | |
if (typeof onBlur === 'function') { | |
onBlur(e) | |
} | |
} | |
static getDerivedStateFromProps (nextProps, prevState) { | |
if (nextProps.children.props.value !== prevState.currentValue && !prevState.isFocused) { | |
return {currentValue: nextProps.children.props.value} | |
} | |
return null | |
} | |
render () { | |
return React.cloneElement(this.props.children, { | |
value: this.state.currentValue, | |
onChange: this.handleChange, | |
onFocus: this.handleFocus, | |
onBlur: this.handleBlur | |
}) | |
} | |
} | |
InputGuard.defaultProps = { | |
type: 'string', | |
notZero: false | |
} | |
InputGuard.propTypes = { | |
children: PropTypes.element.isRequired, | |
type: PropTypes.oneOf(['int', 'float', 'naturalNumber', 'string']), | |
notZero: PropTypes.bool | |
} | |
export default InputGuard |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment