Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
155 views
in Technique[技术] by (71.8m points)

reactjs - react day picker each day rerenders after selection change

I am using react-day-picker (http://react-day-picker.js.org/) and im having troubles with performance. I want to display a huge amount of calendars which are then hidden and scrolled by scrollbar. However, when amount of calendars is too big, selecting days works too slow (for circa 10 years displayed it takes about 1second, sometimes even more). I figured out, that probably the reason why this happens is that after selecting days in calendar each day rerenders (and for 10*365 days it does make a difference). How can i prevent days from rerendering? I know that i should probably use useMemo hook, but im not exactly sure how

BigCalendar code:

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(), end.getMonth(), 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, dayTypesData, 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;

Day code:

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ń, to ustawiamy górn?, doln? i praw? ramk?
        else if (compareDates(day, currentModifier.to)) {
  

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...