0

i'd like to achieve open react-datepicker calendar on button click outside of this component with ref. It is working fine if its outside of conditional rendering. If i put into a condition statementm i'll get TypeError: this.calendarRef.current is null. It is class component and calendarRef is defined in constructor.

import React from 'react';
import Grid from '@material-ui/core/Grid';
import Button from '@material-ui/core/Button';
import DatePicker from "react-datepicker";

const MyCustomDatePicker = React.forwardRef((props, ref) => (
   <DatePicker
    ref={ref}
    selected={new Date()}
    popperPlacement="bottom-start"
    monthsShown={2}
   />
));

class DateRangeCustom extends React.Component {
    constructor(props) {
       super(props);
       this.state = {
          currentDate: '01/12/2021',
          ddCalendarOn: false,
          endDate: '08/12/2021',
    };

    this.calendarRef = React.createRef();
}

handleCalendarBtnClick = () => {
    console.log('*** HANDLE CALENDAR BTN ***');
    this.setState((state) => ({
        ddCalendarOn: !state.ddCalendarOn
    }));
    this.calendarRef.current.setOpen(true);
}

render() {
    console.log('*** render ****')
    return (
        <Grid container>
            <Grid>
                <Grid container direction="column">
                    <Button
                        onClick={this.handleCalendarBtnClick}
                    >
                        SHOW
                    </Button>
                    {this.state.ddCalendarOn && <MyCustomDatePicker
                        ref={this.calendarRef}
                    />}                        
                </Grid>
            </Grid>
        </Grid>
    );
  }
}

export default DateRangeCustom;

Thank you for any advice.

3
  • Why do you declare MyCustomDatePicker inside another component? Where is ay conditional rendering occurring that you are referring to? Can you include a full component example so we may see everything that touches that calendarRef? Commented Jan 30, 2021 at 23:06
  • @DrewReese thank you, my fault with copying and cleaning my code, for easyiest example. Is there better way how to get ref to child component? i found it in some example that's the reason :) i'm new in react and still learning. Thank you for any help:) Commented Jan 30, 2021 at 23:36
  • @DrewReese you were right, no need for ref or some inner component declaration. Commented Jan 31, 2021 at 10:08

2 Answers 2

0

The ref is registered on the component once rendered. But you render it conditionally with: this.state.ddCalendarOn and then try to call it imperativly directly. This will not work because a state change will cause a render and only then the ref is registered.

I would try to use a more declarative approach, I would think that the API allows that. Something like:

<MyCustomDatePicker ref={this.calendarRef} open={this.state.ddCalendarOn}/>

Then you handle the open state on the Callendar component itself. React has in mind to use this declarative approach whenever possible and only seldomly you will have to use a ref to imperativly call something. The Callendar will not render when you set its "open" prop to false, the result should be the same.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, Chrissi, i found out there's no nead for ref or custom picker... beginners mistake... Your answer pushed me to correct direction. Thank you :)
0

My bad,

pretty easy thing and stupid mistake. There's no need for using ref or forwardRef. Just set open to the same variable as is in condition for rendering makes it work.

import React from 'react';
import Grid from '@material-ui/core/Grid';
import Button from '@material-ui/core/Button';
import DatePicker from "react-datepicker";

class DateRangeCustom extends React.Component {
    constructor(props) {
       super(props);
       this.state = {
          currentDate: '01/12/2021',
          ddCalendarOn: false,
          endDate: '08/12/2021',
    };
}

handleCalendarBtnClick = () => {
    console.log('*** HANDLE CALENDAR BTN ***');
    this.setState((state) => ({
        ddCalendarOn: !state.ddCalendarOn
    }))
}

render() {
    console.log('*** render ****')
    return (
        <Grid container>
            <Grid>
                <Grid container direction="column">
                    <Button
                        onClick={this.handleCalendarBtnClick}
                    >
                        SHOW
                    </Button>
                    {this.state.ddCalendarOn && <DatePicker
                       open={this.state.ddCalendarOn}
                       selected={new Date()}
                       popperPlacement="bottom-start"
                       monthsShown={2}
                    />}                        
                </Grid>
            </Grid>
        </Grid>
    );
  }
}

export default DateRangeCustom;

4 Comments

Yes, this was what I was refering to. But you can omit " {this.state.ddCalendarOn && " as well. I would think that the calendar is only rendered when open so there is no loss in removing this conditional rendering.
@ChrissiGrilus no, if i omit that statement, input textfield of datepicker will be rendered by default. set flag to open cause that datepicker will be rendered with input and also calendar.
That is weird but as long as it works for you thats great. Which UI lib are you using?
@ChrissiGrilus Most of the time Material UI

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.