如何解决每天在选择更改后重新渲染日期选择器
我正在使用 react-day-picker (http://react-day-picker.js.org/) 并且我遇到了性能问题。 我想显示大量日历,然后通过滚动条隐藏和滚动这些日历。但是,当日历数量太大时,选择日期的速度太慢(显示大约 10 年需要大约 1 秒,有时甚至更长)。我想通了,发生这种情况的原因可能是在日历中选择天数后,每天都会重新渲染(并且对于 10*365 天确实有所不同)。如何防止几天重新渲染?我知道我可能应该使用 useMemo 钩子,但我不确定如何
BigCalendar 代码:
import React,{ useState,useEffect,useMemo } from 'react';
import DayPicker,{ DateUtils } from 'react-day-picker';
import '../styles/Calendar.scss';
import 'react-day-picker/lib/style.css';
import getStyles from '../utils/CalendarSizes';
import Weekday from './calendar-elements/Weekday';
import Caption from './calendar-elements/Caption';
import Day from './calendar-elements/Day';
import { compareDates } from '../utils/DateUtils';
/**
* Funkcja dodająca dni spełniające warunek do danego modyfikatora stylu
* @param {object} modifier poprzedni modyfikator
* @param {Array} data tablica z danymi dotyczących dni
*/
const addDataToModifiers = (modifier,data) => {
data.forEach(data => modifier[data.Name] = /*{from: data.from,to: data.to}*/ data.Days)
return modifier;
}
/**
* Funkcja dodająca styl do modifikatora
* @param {object} modifier poprzednie style modyfikatora
* @param {Array} data tablica danymi dotycząca kolorów komórek typu dnia
*/
const addStylesToDayTypesModifiers = (modifier,data) => {
data.forEach(data => modifier[data.Name] = { backgroundColor: data.Color });
return modifier;
}
/**
* Funkcja zwracająca ilośc miesięcy pomiędzy dwiema datami
* @param {Date} from pierwsza data
* @param {Date} to druga data
*/
const calculateNumberOfMonths = (from,to) => {
let fromMonthValue = from.getMonth();
let toMonthValue = to.getMonth();
let fromYearValue = from.getYear();
let toYearValue = to.getYear();
return (toYearValue - fromYearValue) * 12 + (toMonthValue - fromMonthValue) + 1;
}
/**
* Funkcja dodająca styl do modifikatora
* @param {object} modifier poprzednie style modyfikatora
* @param {Array} data tablica danymi dotycząca kolorów ramek komórek typu rozkładu
*/
const addStylesToScheduleTypesModifiers = (modifier,data) => {
data.forEach(data => modifier[data.Name] = { borderColor: data.Color });
return modifier;
}
/**
* Funkcja zwracająca tablice dat w podanym przedziale (obustronnie domknięta)
* @param {Date} from początek przedziału
* @param {Date} to koniec przedziału
*/
const getDaysBetweenDates = (start,end) => {
if (!start || !end) {
return [];
}
if (start === end) {
return [start];
}
for (var arr = [],dt = new Date(start); dt <= end; dt.setDate(dt.getDate() + 1)) {
arr.push(new Date(dt));
}
// return arr.map(date => ({ Date: date }));
return arr;
};
const getdisabledDays = (start,end) => {
let disabledDays = [];
for (let i = 1; i < start.getDate(); i++) {
disabledDays.push(new Date(start.getFullYear(),start.getMonth(),i));
}
let endDate = new Date(end.getFullYear(),end.getMonth(),0).getDate();
for (let i = end.getDate() + 1; i <= endDate; i++) {
disabledDays.push(new Date(end.getFullYear(),i));
}
return disabledDays;
}
const getModifiers = (dayTypesData,scheduleTypesData) => {
let modifiers = {
all: { daysOfWeek: [0,1,2,3,4,5,6] }
}
modifiers = addDataToModifiers(modifiers,dayTypesData);
modifiers = addDataToModifiers(modifiers,scheduleTypesData);
return modifiers;
}
const BigCalendar = (props) => {
const { fromMonth,toMonth,dayTypesData,scheduleTypesData,updatedTypeColor,selectedDaysCallback,orgUnitId,...rest } = props;
const [selectedDays,setSelectedDays] = useState([]);
const [firstClickedDay,setFirstClickedDay] = useState(null);
let modifiers = useMemo(() => getModifiers(dayTypesData,scheduleTypesData),[dayTypesData,scheduleTypesData]);
const disabledDays = getdisabledDays(fromMonth,toMonth);
useEffect(() => {
setSelectedDays([]);
setFirstClickedDay(null);
selectedDaysCallback([]);
},[orgUnitId])
/**
* Funkcja zaznaczająca dni w kalendarzu
* @param {any} day kliknięta data
* @param {any} e obiekt zdarzenia
*/
const handleDaySelected = (day,{ },e) => {
let newValue;
if (disabledDays.some(disabledDay => compareDates(disabledDay,day))) {
return;
}
//zwykłe kliknięcie: zaznaczamy dzień i usuwamy resztę zaznaczenia
if ((!(e.shiftKey || e.ctrlKey))) {
newValue = {
from: day,to: day
};
newValue = [...getDaysBetweenDates(newValue.from,newValue.to)];
}
//kliknięcie z shiftem: zaznaczamy przedział
else if (e.shiftKey) {
let newFirstClickedDay = null;
//jeśli pierwszy dzień przedziału nie został wybrany,to go ustawiamy
if (!firstClickedDay) {
if (selectedDays.find(selDay => compareDates(selDay,day))){
newValue = {
from: null,to: null
}
}
else {
newValue = {
from: day,to: day,};
newFirstClickedDay = day;
}
}
//jeśli został,to obecnie wybrany dzień jest końcem lub początkiem przedziału
else {
let [from,to] = [null,null];
if (firstClickedDay.getTime() <= day.getTime()) {
let beginDate = new Date(firstClickedDay);
beginDate.setDate(firstClickedDay.getDate() + 1);
[from,to] = [beginDate,day]
}
else {
let endDate = new Date(firstClickedDay);
endDate.setDate(firstClickedDay.getDate() - 1);
[from,to] = [day,endDate];
}
newValue = {
from: from,to: to,};
}
newValue = [...selectedDays,...getDaysBetweenDates(newValue.from,newValue.to)];
setFirstClickedDay(newFirstClickedDay);
}
// kliknięcie z ctrlem: dodanie/usunięcie pojedynczego dnia
else if (e.ctrlKey) {
let selectedDay = selectedDays.find(selDay => compareDates(selDay,day));
//jeżeli dzień nie był zaznaczony,to go dodajemy
if (!selectedDay) {
newValue = [...selectedDays,day];
}
//w przeciwynym wypadku usuwamy,jeśli był kliknięty z shiftem,to usuwamy też z tej pozycji
else {
newValue = selectedDays.filter(selDay => !compareDates(selDay,day));
if (newValue.length === 0 || (firstClickedDay && compareDates(day,firstClickedDay))) {
setFirstClickedDay(null);
}
}
}
newValue = [...new Set(newValue.sort((a,b) => a.getTime() - b.getTime()))];
//console.log(newValue);
setSelectedDays(newValue);
selectedDaysCallback(newValue);
}
//funkcja zwracająca początkową wartość modyfikatorów
const getinitialModifiers = () => {
let modifieRSStyles = {
all: getStyles(props.numberOfMonths).modifieRSStyle.all,}
modifieRSStyles = addStylesToDayTypesModifiers(modifieRSStyles,props.dayTypesData);
modifieRSStyles = addStylesToScheduleTypesModifiers(modifieRSStyles,props.scheduleTypesData);
return modifieRSStyles;
}
const [modifieRSStyles,setModifieRSStyles] = useState(getinitialModifiers())
useEffect(() => {
setModifieRSStyles(getinitialModifiers());
},[updatedTypeColor,scheduleTypesData])
let numberOfMonths = calculateNumberOfMonths(fromMonth,toMonth);
return (
<div>
<div {...rest} className={'calendar-flex-container'}>
<DayPicker
modifiers={modifiers}
modifieRSStyles={modifieRSStyles}
className="calendar-flex-item"
month={fromMonth}
numberOfMonths={numberOfMonths}
onDayClick={handleDaySelected}
selectedDays={selectedDays}
canChangeMonth={false}
weekdayElement={<Weekday numberOfMonths={numberOfMonths} />}
captionElement={<Caption numberOfMonths={numberOfMonths} />}
renderDay={(day,currentModifiers) =>
<Day day={day}
currentModifiers={currentModifiers}
modifieRSStyles={modifieRSStyles}
allModifiers={modifiers} />}
disabledDays={disabledDays}
/>
</div>
</div>
)
}
export default BigCalendar;
日期代码:
import React from 'react';
import '../../styles/Day.scss';
import { compareDates } from '../../utils/DateUtils';
/**
* Funkcja zwracająca styl wewnętrnej części komórki dnia
* @param {object} currentModifiers modyfikatory do których należy obecny dzień
* @param {object} modifieRSStyles style modyfikatorow
*/
const getInnerCombinedStyle = (currentModifiers,modifieRSStyles) => {
let modifiersArray = Object.entries(currentModifiers);
let modifieRSStylesMap = new Map(Object.entries(modifieRSStyles));
let resultingStyle = modifiersArray
.filter(([key,value]) => value)
.map(([key,value]) => modifieRSStylesMap.get(key))
.reduce((a,b) => ({ ...a,...b }),{})
return resultingStyle;
}
/**
* Funkcja tworząca ramkę wokół komórki dnia
* @param {Date} day dzień któremu tworzona jest ramka
* @param {object} modifier modyfikator odpowiadający za ramkę
* @param {object} allModifiers wszystkie modyfikatory
* @param {object} modifieRSStyleMap mapa styli modyfikatorów
*/
const createBorder = (day,modifier,allModifiers,modifieRSStylesMap) => {
let [key,value] = modifier;
let style = modifieRSStylesMap.get(key);
let currentModifier = allModifiers[key];
//console.log("day render");
if (!style) {
return style;
}
if (currentModifier && currentModifier.from && currentModifier.to) {
//jeżeli jest to pierwszy dzień przedziału,to ustawiamy górną,dolną oraz lewą ramkę
if (compareDates(day,currentModifier.from)) {
let borderBottom = { borderBottomColor: style.borderColor};
return { borderLeftColor: style.borderColor,borderTopColor: style.borderColor,...borderBottom,borderRight: 0 };
}
// jeśli jest to ostatni dzień,dolną i prawą ramkę
else if (compareDates(day,currentModifier.to)) {
let borderTop = { borderTopColor: style.borderColor };
return { borderRightColor: style.borderColor,...borderTop,borderBottomColor: style.borderColor,borderLeft: 0 };
}
}
//w przeciwnym wypadku ustawiamy górną i dolną ramkę
let borderBottom = { borderBottomColor: style.borderColor };
return { borderTopColor: style.borderColor,borderLeft: 0,borderRight: 0 }
}
/**
* Funkcja zwracająca styl zewnętrnej części komórki dnia
* @param {object} currentModifiers modyfikatory do których należy obecny dzień
* @param {object} modifieRSStyles style modyfikatorow
* @param {object} allModifiers wszystkie modyfikatory
* @param {day} day data odpowiadająca komórce dnia
*/
const getouterCombinedStyle = (currentModifiers,modifieRSStyles,day) => {
let modifiersArray = Object.entries(currentModifiers);
let modifieRSStylesMap = new Map(Object.entries(modifieRSStyles));
//tworzymy ramkę,potem scalamy style w jeden
let resultingStyle = modifiersArray
.filter(([key,value]) => value)
.map((modifier) => createBorder(day,modifieRSStylesMap))
.reduce((a,{})
return resultingStyle;
}
const Day = ({ day,currentModifiers,allModifiers }) => {
return (
<div style={{ margin: '2px 0' }}>
<div className='outer' style={getouterCombinedStyle(currentModifiers,day)}>
<div className='inner' style={getInnerCombinedStyle(currentModifiers,modifieRSStyles)}>
{day.getDate()}
</div>
</div>
</div>
)
}
export default Day;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。