对渲染进行两次循环渲染

时间:2020-01-15 19:02:24

标签: javascript reactjs typescript momentjs

我正在尝试制作带有时间的简单日历。第一次加载完美,但是当单击上一个或下一个渲染中的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
  }
}

3 个答案:

答案 0 :(得分:2)

我遇到了你的问题。由于在key函数上设置Week组件的renderWeeks道具的方式,您遇到了这个问题。 1个星期从一个月到另一个月重复一次,因此当您更改月份时,有2个Week组件以相同的key道具结束。反应,在重新渲染组件时,请查看并回收该组件。

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

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组件以避免他重新渲染第一周,或者可以使用加载组件(错误的UX)清除所有日历状态。有很多选择。

我的解决方案是将星期存储在Calendar组件状态,然后仅用新的星期进行更新。

答案 1 :(得分:1)

周中的循环-执行 4 次,因此键入的日期重复。<​​/ p>

[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”-应该计算

enter image description here

答案 2 :(得分:0)

问题似乎出在renderWeeks()的{​​{1}}函数中。

Calendar.tsx行中,尝试将done = count++ > 2 && monthIndex !== date.month();移动到++count)的前面,以便在与++count比较之前将其递增。或将2减少为2

当我尝试更改任何内容时,StackBlitz不适用于我,所以我无法使用它(虽然很酷的网站)。