Trigger React Pikaday calendar from an icon onClick

I have an app that uses React Pikaday, which is a JS calendar plugin that adds a popup calendar and is bound to an HTML <input> tag. Since it’s in React, the wrapper component is called <DatePicker>. Source code

I also need to add a little FontAwesome calendar icon to the input box. But because the <DatePicker> is an Date input field, I can’t add the calendar icon as a text value. Currently I have the <i> icon positioned after the <DatePicker> component and then padding applied to have it move back slightly.

My render() function looks like

<DatePicker>
    format="MM/DD/YYYY"
    value={this.state.date}
    onOpen={this.handleCalendarOperations}
    onDraw={this.handleCalendarOperations}
    onChange={this.handleCalendarOperations}
</DatePicker>

<i className="fa fa-calendar" id="CalculatorCalendar" title="Toggle Calendar"></i>

However, it is also necessary for a click action on the icon to open the calendar. But the calendar functionality is bound to the input field, so I have no way of accessing the lower level functionality. I found a post on SO, Triggering Pikaday date picker script on input field and icon, and the solution is basically to add an ID value to the <i> tag, which I did, and then to use the following

document.getElementById("CalculatorCalendar").addEventListener("click", function(){
    picker.show();
});

to trigger the Pikaday calendar. But, the problem is this is React. And the linked solution requires the instantiation of a new Pikaday instance in vanilla JS. That’s what the picker.show() is from. The solution uses a new Pikaday() instance and calls picker.show() on it.

I need to do what this solution does, but in React. What would be the “React way” to open my DatePicker calendar by clicking the associated icon? Thanks.

Answer

Turns out using the third party component wrapper around Pikaday hid too many of the abstractions that I needed access to, like the Pikaday .show() function. So I wound up refactoring my code to use the vanilla JS implementation of Pikaday, and then modified the linked solution from my initial post so it would work with React.createRef().

constructor(props){
    this.fieldRef = React.createRef(),
}

componentDidMount(){
    this.pikaday = new Pikaday({
            field: this.fieldRef.current,
            format: 'MM/DD/YYYY',
            onOpen: this.handleCalendar,
            onDraw: this.handleCalendar,
            onChange: this.changeColors
    });
}

render(){
    return(
        <input 
            type="text" 
            id="datepicker" 
            ref={this.fieldRef} 
            value={this.state.date}
        ></input>
        <i class="fa fa-calendar" onClick={() => this.pikaday.show()} />
    );
}

The fieldRef is what Pikaday uses to reference the element that it binds to, so you initialize the reference in the app constructor, and add this.fieldRef as a ref attribute to the <input> tag. I then instantiated the Pikaday calendar in componentDidMount() so the render operation will have already completed, allowing the Pikaday() constructor to have a ref field ready to bind to. And in the Pikaday constructor’s parameters, link the field parameter with this.fieldRef.current so Pikaday binds to the current instance of the ref.