【问题标题】:react render for loop run twice反应渲染循环运行两次
【发布时间】:2020-01-15 19:02:24
【问题描述】:

我正在尝试用时刻制作简单的日历。它第一次加载完美,但是当单击上一个或下一个我的 for 循环在渲染中运行两次时可能是问题

My project https://stackblitz.com/edit/react-ts-itbzcd

    public render() {
        let day = this.props.date
        for (let i = 0; i < 7; i++) {
          this.days.push(
            <Day key={day.toString()} date={day.date()} />
          );
          day = day.clone();
          day.add(1, "day");
        }
    console.log(this.days)
    return (
      <tr key={this.days[0]}>
        {this.days}
      </tr>
    );
  }

完成Calendar组件:

import moment = require('moment');
import * as React from 'react'

import { DayNames, Week } from '.'
import styles from './Calendar.module.scss'

interface ICalendarProps {
}

interface ICalendarState {
  dateObject: moment.Moment
  showYearTable: boolean
}
export class Calendar extends React.Component<ICalendarProps, ICalendarState> {
  constructor(props: ICalendarProps) {
    super(props)
    this.state = {
      dateObject: moment(),
      showYearTable: false
    }

    this.onNext = this.onNext.bind(this)
    this.onPrev = this.onPrev.bind(this)
  }
  public render(): React.ReactElement<ICalendarProps> {
    const datePicker =
      <div className="datepicker-days">
        <table className={styles.table}>
          <thead>
            <tr>
              <th><span className="ms-Icon ms-Icon--ChevronLeft" title="Previous Month" onClick={this.onPrev}>Previous</span></th>
              <th className={styles["picker-switch"]} data-action="pickerSwitch" colSpan={5} title="Select Month">
                {this.month()}{" "} {this.year()}
              </th>
              <th><span className="ms-Icon ms-Icon--ChevronRight" title="Next Month" onClick={this.onNext}>Next</span></th>
            </tr>
            <tr>
              <DayNames />
            </tr>
          </thead>
          <tbody>
            {this.renderWeeks()}
          </tbody>
        </table>
      </div>

    return (
      <div className="datepicker">
        {datePicker}
      </div>
    )
  }

  private month = () => {
    return this.state.dateObject.format('MMMM')
  }
  private year = () => {
    return this.state.dateObject.format("Y");
  };
  private onPrev = () => {
    this.setState({
      dateObject: this.state.dateObject.subtract(1, this.state.showYearTable === true ? "year" : "month")
    });
  };
  private onNext = () => {
    this.setState({
      dateObject: this.state.dateObject.add(1, this.state.showYearTable === true ? "year" : "month")
    });
  };

  private renderWeeks() {
    let weeks = [];
    let date = this.state.dateObject.clone().startOf("month").add("w").day("Sunday");
    let done = false;
    let count = 0;
    let monthIndex = date.month();

    while (!done) {
      weeks.push(
        <Week key={date.toString()} date={date.clone()} />
      )
      date.add(1, "w");
      done = count++ > 2 && monthIndex !== date.month();
      monthIndex = date.month();
    }
    return weeks
  }
}

【问题讨论】:

    标签: javascript reactjs typescript momentjs


    【解决方案1】:

    我有你的问题。您遇到此问题是因为您如何在 renderWeeks 函数上设置 Week 组件的 key 属性。 1 周从一个月到另一个重复,因此当您更改月份时,2 个 Weeks 组件以相同的 key 属性结尾。 React,何时重新渲染您的组件,查看并回收该组件。

    这样做可以解决您的问题:

    private renderWeeks() {
        let weeks = [];
        let date = this.state.dateObject.clone().startOf("month").add("w").day("Sunday");
        let done = false;
        let count = 0;
        let monthIndex = date.month();
    
        while (!done) {
          weeks.push(
            <Week key={Math.floor(Math.random() * 10000)} date={date.clone()} />
          )
          date.add(1, "w");
          done = count++ > 2 && monthIndex !== date.month();
          monthIndex = date.month();
        }
        return weeks
      }
    

    但这可能会降低您的重新渲染性能,您需要处理您的Calendar 组件以避免他在第一周重新渲染,或者您可以使用加载组件清除所有日历状态(糟糕的用户体验)。有很多选择。

    我的解决方案是将周存储在 Calendar 组件状态中,然后仅使用新的周更新它。

    【讨论】:

      【解决方案2】:

      Week 循环 - 执行 4 次,因此天数重复。

      [0,1,2,3,4,5,6] 然后 [0,1,2,3,4,5,6] 然后 [0,1,2,3,4,5,6]然后 [0,1,2,3,4,5,6]

      所以 21 天有重复的密钥。


      好的,优化的解决方案,基本上可以工作。 (在月份之间跳转时会出现问题) - 您是否需要编写一个条件来检查该月的天数并根据它停止循环? 你可以像这样解决这个问题 - 循环中的循环:

      private renderWeeks() {
          let weeks = [];
          let date = this.state.dateObject.clone().startOf("month").add("w").day("Sunday");
          let count = 0;
          let monthIndex = date.month();
          let day = date;
      
          for (let i = 0; i < 4; i++) {
              weeks.push(<Week key={date.toString()} date={date.clone()} />)
              date.add(1, "w");
              monthIndex = date.month();
              for (let j = 0; j < 7; j++) {
                  weeks.push(<Day key={weeks.length} date={day.date()} />);
                  day = day.clone();
                  day.add(1, "day");
              }
          }
          return weeks
      }
      

      Day.tsx

      import * as React from 'react'
      
      interface IDayProps {
          date: number
      }
      
      
      export class Day extends React.Component<IDayProps, {}> {
      
          public render() {
              return (
                  <td>{this.props.date}</td>
              );
          }
      }
      

      你也可以给键:

      <Day key={Math.random()} date={day.date()} />
      

      效果最佳 - 问题是:

      • 断月:
      • 第一个循环中的第一个条件是“4” - 应该计算

      【讨论】:

      • 这不是一个好主意,因为它会重复日期,因为日历呈现错误的日期
      • 嘿 Milind - 这个 callendar 渲染的日期与您在评论中提到的相同 - 根据您提供的内容工作,因此在这种情况下它也很糟糕
      【解决方案3】:

      看起来问题出在Calendar.tsx 中的renderWeeks() 函数中。

      done = count++ &gt; 2 &amp;&amp; monthIndex !== date.month(); 行中,尝试将++ 移动到count (++count) 的前面,以便在与2 进行比较之前递增。或将2 缩减为1

      当我尝试更改任何内容时,StackBlitz 对我不起作用,因此我无法使用它(不过是非常酷的网站)。

      【讨论】:

        猜你喜欢
        • 2020-08-16
        • 2022-11-12
        • 2020-06-29
        • 1970-01-01
        • 2020-08-17
        • 2011-03-04
        • 2017-07-14
        • 2020-07-04
        • 1970-01-01
        相关资源
        最近更新 更多