微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

每天在选择更改后重新渲染日期选择器

如何解决每天在选择更改后重新渲染日期选择器

我正在使用 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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?