简体   繁体   English

反应渲染循环运行两次

[英]react render for loop run twice

I am trying to make simple calendar with moment.我正在尝试用时刻制作简单的日历。 It load perfect on first time but when click on previous or next my for loop in render run twice what can be the issue它第一次加载完美,但是当单击上一个或下一个我的 for 循环在渲染中运行两次时可能是什么问题

My project https://stackblitz.com/edit/react-ts-itbzcd 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>
    );
  }

Complete Calendar component:完整的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
  }
}

I got your problem.我得到了你的问题。 You are having this problem because of how you are setting the key prop of the Week component on the renderWeeks function.你有,因为你是如何设置的这个问题key的道具的Week上的组件renderWeeks功能。 1 week repeats from a month to another, so when you change the month, 2 Week s components end with the same key prop. 1 周从一个月到另一个月重复,因此当您更改月份时, 2 Week的组件以相同的key属性结束。 React, when is rerendering your component, see that and recycle that component. React,何时重新渲染您的组件,请查看并回收该组件。

Doing something like this solves your problem:这样做可以解决您的问题:

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
  }

But it could decrease your rerendering performance, you need to deal with your Calendar component to avoid him to rerender the first week or you can clean all of your calendar states with a loading component (bad UX).但这可能会降低您的重新渲染性能,您需要处理您的Calendar组件以避免他在第一周重新渲染,或者您可以使用加载组件(糟糕的用户体验)清理所有日历状态。 There are many alternatives.有很多选择。

My solution would be storing the weeks on the Calendar component state, then update it with just the new weeks.我的解决方案是将周数存储在Calendar组件状态中,然后仅使用新周数对其进行更新。

The loop in Week - execute 4 times, so keys in days duplicate.周中的循环 - 执行4次,因此天数重复。

[0,1,2,3,4,5,6] then [0,1,2,3,4,5,6] then [0,1,2,3,4,5,6] then [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] 然后 [0] ,1,2,3,4,5,6]

So 21 days have duplicated key.所以21天有重复的密钥。


Ok, optimalized solution and basically works.好的,优化的解决方案,基本上有效。 (the problem appears when jumping between months) - do you need to write a condition that will check the number of days in the month and based on it stop the loops? (在月份之间跳转时会出现问题) - 您是否需要编写一个条件来检查当月的天数并根据它停止循环? You can solve this like this - loop in loop:您可以像这样解决这个问题 - 循环循环:

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
}

And in Day.tsxDay.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>
        );
    }
}

You also can give for key :你也可以给 key :

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

It works optimally - the problem is:它以最佳方式工作 - 问题是:

  • cutting off months:截断月份:
  • the 1st condition is "4" in 1st loop - should be calculate第一个条件是第一个循环中的“4” - 应该计算

在此处输入图片说明

It looks like the problem is in your renderWeeks() function in Calendar.tsx .看起来问题出在Calendar.tsx中的renderWeeks()函数中。

In the line done = count++ > 2 && monthIndex !== date.month();done = count++ > 2 && monthIndex !== date.month(); , try moving the ++ to the front of count ( ++count ) so that it is incremented BEFORE being compared to 2 . , 尝试将++移动到count ( ++count ) 的前面,以便在与2进行比较之前增加它。 Or reduce 2 to 1 .或者减少21

StackBlitz wasn't working for me when I tried altering anything so I wasn't able to play around with it (pretty cool site though).当我尝试改变任何东西时,StackBlitz 对我不起作用,所以我无法使用它(虽然很酷的网站)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM