Hi,
I have a currency field where the prefix can be dynamic (as they can select a currency of their choosing):
<Cleave style={{width: '100%'}} className="borderless simplebox" options={{ numeral: true, numeralThousandsGroupStyle: 'thousand', numeralDecimalScale: 2, numeralPositiveOnly: true, rawValueTrimPrefix: true, prefix: target_currency.symbol }} max="10000" onInit={this.initTarget} onChange={this.changeTarget} />
However, the cleave does not re-render to reflect this change. Is there a way round this? @nosir
Thanks,
Dan
Hey @dmason30 Unfortunately cleave doesn't support dynamically updating options (except only one option: phoneRegionCode).
I guess in your case, the only way is to create multiple cleave components with different prefix option, and switch to displaying them based on the target_currency.symbol prop or state change :face_with_head_bandage:
That would be a nice feature to have!
Here is my workaround with common JS (and jQuery).
const INPUT = '#cleave_input';
var CLEAVE;
function getTheNewPrefix() {
return 'whatever-dynamic-prefix';
}
function updateCleave() {
var new_prefix = getTheNewPrefix();
if (CLEAVE instanceof Cleave) {
var previous_prefix = CLEAVE.properties.prefix;
CLEAVE.destroy();
var previous_prefix_re = new RegExp('^' + previous_prefix);
var new_value = $(INPUT).val().replace(previous_prefix_re, new_prefix);
$(INPUT).val(new_value);
}
CLEAVE = new Cleave(INPUT, {
prefix: new_prefix,
blocks: [new_prefix.length, 4, 4],
delimiter: '-',
// ...
});
}
updatePrefix() destroys cleave if needed, replaces the previous prefix by the new one and rises cleave back on.
This could surely be improved (still a workaround though)
It would be really nice if cleave supported updating options.
But FWIW, in react you can change the component's key to force a re-init on cleave with your new options. It is kind of the nuclear option as it blows away the og <input/> element and changes focus. But as long as you're not running this each time they type a character in the box, you're probably ok:
let ID = 1
class CleaveWrapper extends React.Component {
state = {
key: ID++,
options: null,
}
componentWillReceiveProps (nextProps) {
const lastOptions = this.state.options
if (!lastOptions || nextProps.value !== this.props.value) {
const update = { options: getOptions(nextProps) }
if (lastOptions && !deepEqual(update.options, lastOptions)) {
update.key = ID++
}
// Re-init will kill focus
const refocus = update.key && this.input && this.input === document.activeElement
this.setState(update, () => {
if (refocus && this.input) this.input.focus()
})
}
}
render () {
const options = this.state.options || getOptions(this.props)
return (
<CleaveInput
key={this.state.key}
options={options}
htmlRef={(el) => { this.input = el }}
{...this.props}
/>
)
}
}
function getOptions (props) {
// ...something that works out the correct options
return options
}
After digging into this a little more, you can just monkey patch the properties. This is nicer from a user perspective.
class CleaveWrapper extends React.Component {
constructor (props) {
super(props)
this.options = getOptions(props)
}
componentWillReceiveProps (nextProps) {
const lastOptions = this.options
if (!lastOptions || nextProps.value !== this.props.value) {
const options = getOptions(nextProps)
if (lastOptions && options.prefix !== lastOptions.prefix) {
this.el.properties.prefix = options.prefix || ''
this.el.properties.prefixLength = this.el.properties.prefix.length
}
this.options = options
}
}
render () {
return (
<CleaveInput
ref={(el) => { this.el = el }}
options={this.options}
/>
)
}
}
function getOptions (props) {
// ...something that works out the correct options
return options
}
Most helpful comment
That would be a nice feature to have!