You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于React Hooks实现日历与预约渲染的优化方案咨询

Optimizing Appointment Rendering in Your React Calendar Component

Hey there! Let's fix that inefficient appointment rendering issue you're dealing with. Traversing your entire appointments array for every calendar cell feels clunky, and we can definitely do better while sticking to React's best practices.

The Core Problem

Right now, your calendarApts component loops through every time slot for every day, and if you were checking for appointments in each cell, that would mean an O(n*m) complexity (n = days, m = time slots) plus repeated iterations over your appointments array. That's unnecessary—we can pre-process the appointments to make lookups instant.

The Solution: Pre-Process Appointments into a Lookup Object

Instead of looping through all appointments for each cell, we'll convert the appointments array into a key-value object where the key is the timestamp of the appointment (matching the timestamp your cells generate). This way, when rendering a cell, we can just look up the timestamp in the object directly (O(1) time) instead of looping through all appointments.

Here's how to implement this:

1. Create a Memoized Appointment Lookup

Add this useMemo hook to pre-process your appointments. It will only re-run when calendar.appointments changes, so no extra computations on every render:

const appointmentsByTimestamp = useMemo(() => {
  if (!calendar.appointments) return {};
  
  // Convert appointments array to an object keyed by timestamp
  return calendar.appointments.reduce((acc, apt) => {
    // Make sure this timestamp matches the format generated by convertStringToTimestamp
    // If your appointment has a date string instead of a unix timestamp, convert it here:
    // const aptTimestamp = moment(apt.dateTime, 'DD/MM/YY HH:mm').unix();
    const aptTimestamp = apt.timestamp; // Replace with your actual timestamp field
    
    acc[aptTimestamp] = apt;
    return acc;
  }, {});
}, [calendar.appointments]);

2. Render Appointments Directly in Cells

Modify your calendarApts cell rendering to use the lookup object. Now you can check if an appointment exists for the cell's timestamp in one step:

const calendarApts = useMemo(() => {
  if (!settings.currentWeek) return;
  
  return settings.currentWeek.map(day => (
    <div className="line" key={day}>
      {timeline.map(time => {
        const cellTimestamp = convertStringToTimestamp(day, time);
        const appointment = appointmentsByTimestamp[cellTimestamp];
        
        return (
          <div 
            key={`${day} ${time}`} 
            time={cellTimestamp} 
            onClick={() => console.log(day, time)}
            className={appointment ? "has-appointment" : ""}
          >
            {/* Render your appointment content here if it exists */}
            {appointment && (
              <div className="appointment-card">
                <span>{appointment.title}</span>
                <span>{appointment.clientName}</span>
              </div>
            )}
          </div>
        );
      })}
    </div>
  ));
}, [settings.currentWeek, timeline, appointmentsByTimestamp]);

Bonus: Fixing Small Issues in Your Code

While we're at it, let's clean up a couple of things that could cause bugs or unnecessary re-renders:

  • Remove setState from useMemo: Your timeline hook uses setSettings inside useMemo, which is a side effect and shouldn't be there. timeline is already a memoized array—you can use it directly instead of storing it in settings:

    const timeline = useMemo(() => {
      let currTime = '10:00';
      const stopTime = '19:00';
      const interval = 15;
      let timesArr = [];
      while (currTime !== stopTime) {
        timesArr.push(currTime);
        currTime = moment(currTime, 'HH:mm').add(interval, 'minute').format('HH:mm');
      }
      return timesArr;
    }, []);
    
  • Fix Stale Closures in useEffect: Your useEffect hooks use the old settings state when updating. Use functional updates to get the latest state:

    useEffect(() => {
      setCalendar(calendarFromStore);
      setSettings(prev => ({ ...prev, currentWeek: getCurrentWeek }));
    }, [calendarFromStore, getCurrentWeek]);
    
    useEffect(() => {
      setSettings(prev => ({ ...prev, currentWeek: getCurrentWeek }));
    }, [settings.diffWeeks, getCurrentWeek]);
    

Why This Works

  • Performance: Pre-processing the appointments is O(k) (k = number of appointments), and each cell lookup is O(1). This cuts down on redundant loops drastically, especially as your appointment list grows.
  • React-Friendly: We're still using state and memoization to drive rendering—no direct DOM manipulation, which aligns with React's principles.
  • Maintainability: The code is cleaner, with clear separation between data processing and rendering.

内容的提问来源于stack exchange,提问作者danivegas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 07:59:06