Skip to content

Commit

Permalink
feat: Memo'd Android component to avoid re-rendering upstream which h…
Browse files Browse the repository at this point in the history
…as a bug (#453)

* Refactored the Android component to use hooks and memo'd it so that it only renders the underlying DateTimePicker once (or on date change)
  • Loading branch information
lukemcgregor committed Aug 4, 2020
1 parent 0ced69c commit 069b2ca
Showing 1 changed file with 71 additions and 67 deletions.
138 changes: 71 additions & 67 deletions src/DateTimePickerModal.android.js
@@ -1,76 +1,80 @@
import React from "react";
import React, { useEffect, useState, memo } from "react";
import PropTypes from "prop-types";
import DateTimePicker from "@react-native-community/datetimepicker";

export class DateTimePickerModal extends React.PureComponent {
static propTypes = {
date: PropTypes.instanceOf(Date),
isVisible: PropTypes.bool,
onCancel: PropTypes.func.isRequired,
onConfirm: PropTypes.func.isRequired,
onHide: PropTypes.func,
maximumDate: PropTypes.instanceOf(Date),
minimumDate: PropTypes.instanceOf(Date),
};
const DateTimePickerModal = memo(({
date,
mode,
isVisible,
onCancel,
onConfirm,
onHide,
...otherProps
}) =>{
const [currentDate, setCurrentDate] = useState(date);
const [currentMode, setCurrentMode] = useState(null);

static defaultProps = {
date: new Date(),
isVisible: false,
onHide: () => {},
};
useEffect(()=>{
if (isVisible && currentMode === null) {
setCurrentMode(mode === "time" ? "time" : "date");
} else if (!isVisible) {
setCurrentMode(null);
}
}, [isVisible, currentMode]);

state = {
currentMode: null,
};
if (!isVisible || !currentMode) return null;

currentDate = this.props.date;
return (
<DateTimePicker
{...otherProps}
mode={currentMode}
value={date}
onChange={(event, date) => {
if (event.type === "dismissed") {
onCancel();
onHide(false);
return;
}
let nextDate = date;
if (mode === "datetime") {
if (currentMode === "date") {
setCurrentMode("time")
setCurrentDate(new Date(date));
return;
} else if (currentMode === "time") {
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
const day = currentDate.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
nextDate = new Date(year, month, day, hours, minutes);
}
}
onConfirm(nextDate);
onHide(true, nextDate);
}}
/>
);
},
(prevProps, nextProps) =>
prevProps.isVisible === nextProps.isVisible
&& prevProps.date === nextProps.date
);

static getDerivedStateFromProps(props, state) {
if (props.isVisible && state.currentMode === null) {
return { currentMode: props.mode === "time" ? "time" : "date" };
} else if (!props.isVisible) {
return { currentMode: null };
}
return null;
}
DateTimePickerModal.propTypes = {
date: PropTypes.instanceOf(Date),
isVisible: PropTypes.bool,
onCancel: PropTypes.func.isRequired,
onConfirm: PropTypes.func.isRequired,
onHide: PropTypes.func,
maximumDate: PropTypes.instanceOf(Date),
minimumDate: PropTypes.instanceOf(Date),
};

handleChange = (event, date) => {
if (event.type === "dismissed") {
this.props.onCancel();
this.props.onHide(false);
return;
}
if (this.props.mode === "datetime") {
if (this.state.currentMode === "date") {
this.currentDate = new Date(date);
this.setState({ currentMode: "time" });
return;
} else if (this.state.currentMode === "time") {
const year = this.currentDate.getFullYear();
const month = this.currentDate.getMonth();
const day = this.currentDate.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
this.currentDate = new Date(year, month, day, hours, minutes);
}
} else {
this.currentDate = date;
}
this.props.onConfirm(this.currentDate);
this.props.onHide(true, this.currentDate);
};
DateTimePickerModal.defaultProps = {
date: new Date(),
isVisible: false,
onHide: () => {},
};

render() {
const { isVisible, date, ...otherProps } = this.props;
const { currentMode } = this.state;
if (!isVisible || !currentMode) return null;
return (
<DateTimePicker
{...otherProps}
mode={currentMode}
value={date}
onChange={this.handleChange}
/>
);
}
}
export {DateTimePickerModal};

0 comments on commit 069b2ca

Please sign in to comment.