Cleave.js: React - dynamic prefix currency

Created on 14 Jun 2017  路  5Comments  路  Source: nosir/cleave.js

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

Most helpful comment

That would be a nice feature to have!

All 5 comments

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
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

CaptainYarb picture CaptainYarb  路  3Comments

qm3ster picture qm3ster  路  4Comments

wcedmisten picture wcedmisten  路  4Comments

gundlev picture gundlev  路  6Comments

shaileshrathod128 picture shaileshrathod128  路  6Comments