جاوااسکریپت - رندر حلقه for در React دوبار اجرا میشود
من دارم تلاش میکنم یک تقویم ساده با moment بسازم. دفعهی اول بهخوبی بارگذاری میشود اما وقتی روی قبلی یا بعدی کلیک میکنم حلقه for من در render دوبار اجرا میشود، مشکل میتواند چه باشد؟
پروژه من 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
}
}
پاسخ شماره ۱
من مشکل شما را فهمیدم. شما این مشکل را به خاطر نحوه تنظیم پراپ key در کامپوننت Week در تابع renderWeeks دارید. یک هفته از یک ماه به ماه دیگر تکرار میشود، بنابراین وقتی ماه را تغییر میدهید، دو کامپوننت Week با همان پراپ 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 ذخیره کنم و سپس فقط با هفتههای جدید آن را بهروز کنم.
پاسخ شماره ۲
حلقه در هفته - ۴ بار اجرا میشود، بنابراین کلیدها در روزها تکرار میشوند.
[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" در حلقه اول است - باید محاسبه شود
