为什么 React 不重新渲染组件,即使它的状态在 API 调用后发生变化
Why React not re-rendering components even if it's state changes after an API call
当组件初始化时,我有一个像这样用空数组初始化的 React 状态
const [upcomingDates, setUpcomingDates] = useState([]);
而这个日历组件依赖于这个状态。 (这是我们主要组件的一个 chld 组件,并且具有这种状态依赖性)
<Calender name="Eat Eggs" dates={upcomingDates} />
组件加载后,我调用 API 并在 API promise resolves
后填充数组
async function refreshPattern()
{
//Call the API
let pattern = await GetRepeatPattern(id);
//Update State
setUpcomingDates(pattern.UpcomingDates);
setNextOccurance(pattern.NextOccurance);
}
这是我的useEffect
useEffect(async () => {
refreshPattern();
},[]);
现在的问题是
- callender 组件仅使用其 props 中的空数组渲染一次
- 即使在我更改状态后 React 也不会重新渲染
- 当我将 'upcomingDates' 放入 useEffect(...,[upcomingDates]) 第二个参数时,React 陷入无限循环
这是来自控制台的 API 响应。记录 'pattern' 变量
如何让日历组件重新渲染?为什么即使我更改了状态,React 也不会重新渲染组件?
注意:这是完整的组件。
export const RepeatSelector = ({ id }) => {
let dropdownPatternMode = null;
const [every, setEvery] = useState(1);
const [timeMode, setTimeMode] = useState(1);
const [course, setCourse] = useState(1);
const [constrain, setConstrain] = useState(1);
const [patternMode, setPatternMode] = useState(1);
const [nextOccurance, setNextOccurance] = useState('Loading...');
const [upComingDates, setUpcomingDates] = useState([]);
const [dayOfMonth, setDayOfMonth] = useState(1);
const [logicalStart, setLogicalStart] = useState(1);
const [logicalDay, setLogicalDay] = useState(1);
const [pattern, setPattern] = useState({
Value: '',
Mode: '',
Constrain: '',
Time: '',
Date: ''
});
const [startDay, setStartDay] = useState(new Date());
const [startTime, setStartTime] = useState('00:00');
const [days, setDays] = useState([
{
selected: false,
name: "SUN"
},
{
selected: false,
name: "MON"
},
{
selected: true,
name: "TUE"
},
{
selected: true,
name: "WED"
},
{
selected: false,
name: "THU"
},
{
selected: false,
name: "FRI"
},
{
selected: false,
name: "SAT"
},
]);
function init() {
dropdownPatternMode = new Choices(document.getElementById('everyCount'), { searchEnabled: false, shouldSort: false });
}
useEffect(async function () {
init();
let response = await GetActivityRepeat(id);
setDayOfMonth(response.DayOfMonth);
days.map((day, index) => {
if (day.name == 'SUN') {
days[index] = { selected: response.Sunday, name: 'SUN' };
}
else if (day.name == 'MON') {
days[index] = { selected: response.Monday, name: 'MON' };
}
else if (day.name == 'TUE') {
days[index] = { selected: response.Tuesday, name: 'TUE' };
}
else if (day.name == 'WED') {
days[index] = { selected: response.Wednesday, name: 'WED' };
}
else if (day.name == 'THU') {
days[index] = { selected: response.Thursday, name: 'THU' };
}
else if (day.name == 'FRI') {
days[index] = { selected: response.Friday, name: 'FRI' };
}
else if (day.name == 'SAT') {
days[index] = { selected: response.Saturday, name: 'SAT' };
}
return days;
});
setDays(days);
setLogicalStart(response.LogicalStart);
setLogicalDay(response.LogicalDay);
setCourse(response.Course);
setEvery(response.PatternValue);
setPatternMode(response.PatternMode);
dropdownPatternMode.setChoiceByValue([response.PatternMode.toString()]);
setStartDay(new Date(response.StartBy));
setConstrain(response.Constrain);
let myTime = response.SpecificTime.split('T')[1];
setStartTime(myTime);
async function refreshPattern()
{
//Call the API
let pattern = await GetRepeatPattern(id);
//Update State
setUpcomingDates(pattern.UpcomingDates);
setPattern(pattern);
setNextOccurance(pattern.NextOccurance);
}
refreshPattern();
setTimeMode(response.TimeMode);
}, []);
async function SaveData() {
let sDate = new Date().toISOString().substring(0, 10) + " 00:00";
let sTime = new Date().toISOString().substring(0, 10) + " 00:00";
try {
sDate = startDay.toISOString().substring(0, 10) + " 00:00";
}
catch
{
store.addNotification({
title: "Start Date Required",
message: "Please choose a start date for this activity",
type: "warning",
insert: "top",
container: "top-right",
animationIn: ["animate__animated", "animate__fadeIn"],
animationOut: ["animate__animated", "animate__fadeOut"],
dismiss: {
duration: 2000,
pauseOnHover: true
}
});
return;
}
try {
sTime = new Date().toISOString().substring(0, 10) + " " + startTime;
}
catch
{
if (timeMode == 2) {
store.addNotification({
title: "Start Time Required",
message: "Please choose a start time for this activity",
type: "warning",
insert: "top",
container: "top-right",
animationIn: ["animate__animated", "animate__fadeIn"],
animationOut: ["animate__animated", "animate__fadeOut"],
dismiss: {
duration: 2000,
pauseOnHover: true
}
});
return;
}
}
var data = {
ActivityId: parseInt(id),
Course: course,
PatternValue: every,
PatternMode: patternMode,
Sunday: days.find((e) => e.name == 'SUN').selected,
Monday: days.find((e) => e.name == 'MON').selected,
Tuesday: days.find((e) => e.name == 'TUE').selected,
Wednesday: days.find((e) => e.name == 'WED').selected,
Thursday: days.find((e) => e.name == 'THU').selected,
Friday: days.find((e) => e.name == 'FRI').selected,
Saturday: days.find((e) => e.name == 'SAT').selected,
Constrain: constrain,
DayOfMonth: dayOfMonth,
LogicalStart: logicalStart,
LogicalDay: logicalDay,
StartBy: sDate,
TimeMode: timeMode,
SpecificTime: sTime,
};
await SaveActivityRepeat(data);
store.addNotification({
title: "Activity Saved",
message: "Data saved successfully. Changes will reflect soon",
type: "success",
insert: "top",
container: "top-right",
animationIn: ["animate__animated", "animate__fadeIn"],
animationOut: ["animate__animated", "animate__fadeOut"],
dismiss: {
duration: 2000,
pauseOnHover: true
}
});
//refreshPattern();
}
const renderRepeatPattern = () => {
return (
<>
<ul className="list-group mt-3">
<li className="list-group-item border-0 d-flex p-4 mb-2 bg-gray-100 border-radius-lg">
<div className="d-flex flex-column">
<h6 className="mb-3 text-sm">Current Configuration</h6>
<span className="mb-2 text-xs">Pattern:
<span className="text-danger font-weight-bold ms-sm-2">{pattern.Value} {pattern.Mode} {pattern.Constrain} {pattern.Time} {pattern.Date}</span>
</span>
<span className="mb-2 text-xs">Next Occurance:
<span className="text-dark ms-sm-2 font-weight-bold">{nextOccurance}</span></span>
</div>
<div className="ms-auto text-end">
<a className="btn btn-link text-primary px-3 mb-0" href="javascript:;"><i className="fas fa-pencil-alt text-dark me-2" aria-hidden="true" />View Calender</a>
</div>
</li>
</ul>
<Calender name="Eat Eggs" dates={upComingDates} />
</>
);
}
const renderDaySelector = () => {
if (patternMode == 1) {
return (
<>
{renderRepeatPattern()}
<hr className="horizontal dark mt-3" />
<label>Start From</label>
<div className="form-check">
<div className="row">
<div className="col-md-12">
<DatePicker value={startDay} onChange={setStartDay} />
</div>
</div>
</div>
</>
);
}
else if (patternMode == 2) {
return (
<>
<div className="col-md-12 mt-3">
<DaySelector data={days} onDataUpdate={(e) => setDays(e)} />
</div>
<>
{renderRepeatPattern()}
<hr className="horizontal dark mt-3" />
<label>Start From</label>
<div className="form-check">
<div className="row">
<div className="col-md-12">
<DatePicker value={startDay} onChange={setStartDay} />
</div>
</div>
</div>
</>
</>
);
}
else if (patternMode == 3) {
return (
<div className="col-12 col-sm-12">
<label>Constrain By</label>
<div className="form-check">
<input checked={constrain === 1} className="form-check-input" type="radio" name="constrainBy" id="radDayOfMonth" onChange={() => setConstrain(1)} />
<label className="custom-control-label" htmlFor="radDayOfMonth">Day of Month</label>
<div className="row">
<div className="col-md-4">
<DayDropdown onSelection={(e) => setDayOfMonth(e)} value={dayOfMonth} />
</div>
</div>
</div>
<div className="form-check">
<input checked={constrain === 2} className="form-check-input" type="radio" name="constrainBy" id="radLogicaly" onChange={() => setConstrain(2)} />
<label className="custom-control-label" htmlFor="radLogicaly">Logicaly</label>
<div className="row">
<div className="col-md-4">
<WeekDropdown onSelection={(e) => setLogicalStart(e)} value={logicalStart} />
</div>
<div className="col-md-4">
<DayNameDropdown onSelection={(e) => setLogicalDay(e)} value={logicalDay} />
</div>
</div>
</div>
{renderRepeatPattern()}
<hr className="horizontal dark mt-3" />
<label>Start From</label>
<div className="form-check">
<div className="row">
<div className="col-md-12">
<DatePicker value={startDay} onChange={setStartDay} />
</div>
</div>
</div>
</div>
);
}
else if (patternMode == 4) {
return (
<>
{renderRepeatPattern()}
<hr className="horizontal dark mt-3" />
<label>Start From</label>
<div className="form-check">
<div className="row">
<div className="col-md-12">
<DatePicker value={startDay} onChange={setStartDay} />
</div>
</div>
</div>
</>
);
}
}
return (
<>
<div className="row">
{/*COURSE*/ }
<div className="col-12 col-sm-12">
<label>Course</label>
<div className="col-12 col-sm-12">
<div className="form-check">
<input checked={course === 1} className="form-check-input" type="radio" name="course" id="radOneTime" onChange={() => setCourse(1)} />
<label className="custom-control-label" htmlFor="radOneTime">One Time</label>
</div>
<div className="form-check">
<input checked={course === 2} className="form-check-input" type="radio" name="course" id="radMultipleTimes" onChange={() => setCourse(2)} />
<label className="custom-control-label" htmlFor="radMultipleTimes">Multiple Times</label>
</div>
</div>
</div>
{/*PATTERN*/}
<label className="custom-control-label mt-3" htmlFor="customRadio1">Running Pattern</label>
<div className="col-md-1">
<label className="form-check-label" htmlFor="flexSwitchCheckDefault">Every</label>
</div>
<div className="col-md-2">
<input className="form-control" type="number" value={every} onChange={e => setEvery(Number(e.target.value))} />
</div>
<div className="col-md-4">
<select className="form-control" id="everyCount" onChange={e=>setPatternMode(Number(e.target.value))}>
<option value="1">Day</option>
<option value="2">Week</option>
<option value="3">Month</option>
<option value="4">Year</option>
</select>
</div>
{renderDaySelector()}
{/*TIME*/}
<label className="mt-3">Set Time</label>
<div className="form-check">
<div className="row">
<div className="col-12 col-sm-12">
<div className="form-check">
<input checked={timeMode === 1} className="form-check-input" type="radio" name="time" id="radAdapt" onChange={() => setTimeMode(1)} />
<label className="custom-control-label" htmlFor="radAdapt">Adapt Automaticaly</label>
</div>
<div className="form-check">
<input checked={timeMode === 2} className="form-check-input" type="radio" name="time" id="radSpecificDate" onChange={() => setTimeMode(2)} />
<label className="custom-control-label" htmlFor="radSpecificDate">Specific Time</label>
{timeMode === 2 && <TimePicker value={startTime} onChange={setStartTime} />}
</div>
</div>
</div>
</div>
</div >
<div className="card-footer pt-0 p-3 d-flex align-items-center">
<div className="w-60"> <p className="text-sm"> </p>
</div>
<div className="w-40 text-end">
<a className="btn bg-gradient-primary mb-0 text-end" onClick={SaveData}>Save Details</a>
</div>
</div>
</>
);
}
这是日历组件
export const Calender = ({name, dates}) => {
const [upcomingDates, setUpcomingDates] = useState([])
useEffect(() =>
{
let dts = [];
for (let i = 0; i < dates.length; i++) {
dts.push(
{ title: name, date: dates[i], className: 'bg-gradient-dark' }
);
}
setUpcomingDates(dts);
}, []);
return (
<div className="card card-calendar">
<div className="card-body p-3">
<FullCalendar
allDayClassNames="calendar"
plugins={[dayGridPlugin]}
initialView="dayGridMonth"
weekends={false}
events={upcomingDates}
/>
</div>
</div>
); }
这是发生的所有事情以及它不起作用的原因:
第一,当您将状态依赖项添加到 useEffect 时,它会陷入无限循环,因为它调用的函数会修改该状态。
组件在状态改变时肯定会重新渲染,效果如您所料,具有空依赖数组的 useEffect
仅在挂载和卸载时调用。
如果它使用空数组重新渲染,很可能是因为 pattern.UpcomingDates
是一个空数组,而不是因为组件没有按预期运行。您是否尝试记录 pattern.UpcomingDates
是什么?
除此之外,其他人已经注意到您的组件中出现了一些反模式内容。您应该在效果内部声明该函数,或者使用 useCallback
创建它并将其添加到 useEffect
.
的依赖数组中
我发现了问题。
而不是使用 "upcomingDates" 作为父组件 useEffect() 的依赖项
我不得不把它放在子组件(Calender 组件)中,这样它会在日期更改时重新呈现
export const Calender = ({name, dates}) => {
const [upcomingDates, setUpcomingDates] = useState([])
useEffect(() =>
{
let dts = [];
for (let i = 0; i < dates.length; i++) {
dts.push(
{ title: name, date: dates[i], className: 'bg-gradient-dark' }
);
}
setUpcomingDates(dts);
}, [dates]);
return (
<div className="card card-calendar">
<div className="card-body p-3">
<FullCalendar
allDayClassNames="calendar"
plugins={[dayGridPlugin]}
initialView="dayGridMonth"
weekends={false}
events={upcomingDates}
/>
</div>
</div>
);
}
当组件初始化时,我有一个像这样用空数组初始化的 React 状态
const [upcomingDates, setUpcomingDates] = useState([]);
而这个日历组件依赖于这个状态。 (这是我们主要组件的一个 chld 组件,并且具有这种状态依赖性)
<Calender name="Eat Eggs" dates={upcomingDates} />
组件加载后,我调用 API 并在 API promise resolves
后填充数组async function refreshPattern()
{
//Call the API
let pattern = await GetRepeatPattern(id);
//Update State
setUpcomingDates(pattern.UpcomingDates);
setNextOccurance(pattern.NextOccurance);
}
这是我的useEffect
useEffect(async () => {
refreshPattern();
},[]);
现在的问题是
- callender 组件仅使用其 props 中的空数组渲染一次
- 即使在我更改状态后 React 也不会重新渲染
- 当我将 'upcomingDates' 放入 useEffect(...,[upcomingDates]) 第二个参数时,React 陷入无限循环
这是来自控制台的 API 响应。记录 'pattern' 变量
如何让日历组件重新渲染?为什么即使我更改了状态,React 也不会重新渲染组件?
注意:这是完整的组件。
export const RepeatSelector = ({ id }) => {
let dropdownPatternMode = null;
const [every, setEvery] = useState(1);
const [timeMode, setTimeMode] = useState(1);
const [course, setCourse] = useState(1);
const [constrain, setConstrain] = useState(1);
const [patternMode, setPatternMode] = useState(1);
const [nextOccurance, setNextOccurance] = useState('Loading...');
const [upComingDates, setUpcomingDates] = useState([]);
const [dayOfMonth, setDayOfMonth] = useState(1);
const [logicalStart, setLogicalStart] = useState(1);
const [logicalDay, setLogicalDay] = useState(1);
const [pattern, setPattern] = useState({
Value: '',
Mode: '',
Constrain: '',
Time: '',
Date: ''
});
const [startDay, setStartDay] = useState(new Date());
const [startTime, setStartTime] = useState('00:00');
const [days, setDays] = useState([
{
selected: false,
name: "SUN"
},
{
selected: false,
name: "MON"
},
{
selected: true,
name: "TUE"
},
{
selected: true,
name: "WED"
},
{
selected: false,
name: "THU"
},
{
selected: false,
name: "FRI"
},
{
selected: false,
name: "SAT"
},
]);
function init() {
dropdownPatternMode = new Choices(document.getElementById('everyCount'), { searchEnabled: false, shouldSort: false });
}
useEffect(async function () {
init();
let response = await GetActivityRepeat(id);
setDayOfMonth(response.DayOfMonth);
days.map((day, index) => {
if (day.name == 'SUN') {
days[index] = { selected: response.Sunday, name: 'SUN' };
}
else if (day.name == 'MON') {
days[index] = { selected: response.Monday, name: 'MON' };
}
else if (day.name == 'TUE') {
days[index] = { selected: response.Tuesday, name: 'TUE' };
}
else if (day.name == 'WED') {
days[index] = { selected: response.Wednesday, name: 'WED' };
}
else if (day.name == 'THU') {
days[index] = { selected: response.Thursday, name: 'THU' };
}
else if (day.name == 'FRI') {
days[index] = { selected: response.Friday, name: 'FRI' };
}
else if (day.name == 'SAT') {
days[index] = { selected: response.Saturday, name: 'SAT' };
}
return days;
});
setDays(days);
setLogicalStart(response.LogicalStart);
setLogicalDay(response.LogicalDay);
setCourse(response.Course);
setEvery(response.PatternValue);
setPatternMode(response.PatternMode);
dropdownPatternMode.setChoiceByValue([response.PatternMode.toString()]);
setStartDay(new Date(response.StartBy));
setConstrain(response.Constrain);
let myTime = response.SpecificTime.split('T')[1];
setStartTime(myTime);
async function refreshPattern()
{
//Call the API
let pattern = await GetRepeatPattern(id);
//Update State
setUpcomingDates(pattern.UpcomingDates);
setPattern(pattern);
setNextOccurance(pattern.NextOccurance);
}
refreshPattern();
setTimeMode(response.TimeMode);
}, []);
async function SaveData() {
let sDate = new Date().toISOString().substring(0, 10) + " 00:00";
let sTime = new Date().toISOString().substring(0, 10) + " 00:00";
try {
sDate = startDay.toISOString().substring(0, 10) + " 00:00";
}
catch
{
store.addNotification({
title: "Start Date Required",
message: "Please choose a start date for this activity",
type: "warning",
insert: "top",
container: "top-right",
animationIn: ["animate__animated", "animate__fadeIn"],
animationOut: ["animate__animated", "animate__fadeOut"],
dismiss: {
duration: 2000,
pauseOnHover: true
}
});
return;
}
try {
sTime = new Date().toISOString().substring(0, 10) + " " + startTime;
}
catch
{
if (timeMode == 2) {
store.addNotification({
title: "Start Time Required",
message: "Please choose a start time for this activity",
type: "warning",
insert: "top",
container: "top-right",
animationIn: ["animate__animated", "animate__fadeIn"],
animationOut: ["animate__animated", "animate__fadeOut"],
dismiss: {
duration: 2000,
pauseOnHover: true
}
});
return;
}
}
var data = {
ActivityId: parseInt(id),
Course: course,
PatternValue: every,
PatternMode: patternMode,
Sunday: days.find((e) => e.name == 'SUN').selected,
Monday: days.find((e) => e.name == 'MON').selected,
Tuesday: days.find((e) => e.name == 'TUE').selected,
Wednesday: days.find((e) => e.name == 'WED').selected,
Thursday: days.find((e) => e.name == 'THU').selected,
Friday: days.find((e) => e.name == 'FRI').selected,
Saturday: days.find((e) => e.name == 'SAT').selected,
Constrain: constrain,
DayOfMonth: dayOfMonth,
LogicalStart: logicalStart,
LogicalDay: logicalDay,
StartBy: sDate,
TimeMode: timeMode,
SpecificTime: sTime,
};
await SaveActivityRepeat(data);
store.addNotification({
title: "Activity Saved",
message: "Data saved successfully. Changes will reflect soon",
type: "success",
insert: "top",
container: "top-right",
animationIn: ["animate__animated", "animate__fadeIn"],
animationOut: ["animate__animated", "animate__fadeOut"],
dismiss: {
duration: 2000,
pauseOnHover: true
}
});
//refreshPattern();
}
const renderRepeatPattern = () => {
return (
<>
<ul className="list-group mt-3">
<li className="list-group-item border-0 d-flex p-4 mb-2 bg-gray-100 border-radius-lg">
<div className="d-flex flex-column">
<h6 className="mb-3 text-sm">Current Configuration</h6>
<span className="mb-2 text-xs">Pattern:
<span className="text-danger font-weight-bold ms-sm-2">{pattern.Value} {pattern.Mode} {pattern.Constrain} {pattern.Time} {pattern.Date}</span>
</span>
<span className="mb-2 text-xs">Next Occurance:
<span className="text-dark ms-sm-2 font-weight-bold">{nextOccurance}</span></span>
</div>
<div className="ms-auto text-end">
<a className="btn btn-link text-primary px-3 mb-0" href="javascript:;"><i className="fas fa-pencil-alt text-dark me-2" aria-hidden="true" />View Calender</a>
</div>
</li>
</ul>
<Calender name="Eat Eggs" dates={upComingDates} />
</>
);
}
const renderDaySelector = () => {
if (patternMode == 1) {
return (
<>
{renderRepeatPattern()}
<hr className="horizontal dark mt-3" />
<label>Start From</label>
<div className="form-check">
<div className="row">
<div className="col-md-12">
<DatePicker value={startDay} onChange={setStartDay} />
</div>
</div>
</div>
</>
);
}
else if (patternMode == 2) {
return (
<>
<div className="col-md-12 mt-3">
<DaySelector data={days} onDataUpdate={(e) => setDays(e)} />
</div>
<>
{renderRepeatPattern()}
<hr className="horizontal dark mt-3" />
<label>Start From</label>
<div className="form-check">
<div className="row">
<div className="col-md-12">
<DatePicker value={startDay} onChange={setStartDay} />
</div>
</div>
</div>
</>
</>
);
}
else if (patternMode == 3) {
return (
<div className="col-12 col-sm-12">
<label>Constrain By</label>
<div className="form-check">
<input checked={constrain === 1} className="form-check-input" type="radio" name="constrainBy" id="radDayOfMonth" onChange={() => setConstrain(1)} />
<label className="custom-control-label" htmlFor="radDayOfMonth">Day of Month</label>
<div className="row">
<div className="col-md-4">
<DayDropdown onSelection={(e) => setDayOfMonth(e)} value={dayOfMonth} />
</div>
</div>
</div>
<div className="form-check">
<input checked={constrain === 2} className="form-check-input" type="radio" name="constrainBy" id="radLogicaly" onChange={() => setConstrain(2)} />
<label className="custom-control-label" htmlFor="radLogicaly">Logicaly</label>
<div className="row">
<div className="col-md-4">
<WeekDropdown onSelection={(e) => setLogicalStart(e)} value={logicalStart} />
</div>
<div className="col-md-4">
<DayNameDropdown onSelection={(e) => setLogicalDay(e)} value={logicalDay} />
</div>
</div>
</div>
{renderRepeatPattern()}
<hr className="horizontal dark mt-3" />
<label>Start From</label>
<div className="form-check">
<div className="row">
<div className="col-md-12">
<DatePicker value={startDay} onChange={setStartDay} />
</div>
</div>
</div>
</div>
);
}
else if (patternMode == 4) {
return (
<>
{renderRepeatPattern()}
<hr className="horizontal dark mt-3" />
<label>Start From</label>
<div className="form-check">
<div className="row">
<div className="col-md-12">
<DatePicker value={startDay} onChange={setStartDay} />
</div>
</div>
</div>
</>
);
}
}
return (
<>
<div className="row">
{/*COURSE*/ }
<div className="col-12 col-sm-12">
<label>Course</label>
<div className="col-12 col-sm-12">
<div className="form-check">
<input checked={course === 1} className="form-check-input" type="radio" name="course" id="radOneTime" onChange={() => setCourse(1)} />
<label className="custom-control-label" htmlFor="radOneTime">One Time</label>
</div>
<div className="form-check">
<input checked={course === 2} className="form-check-input" type="radio" name="course" id="radMultipleTimes" onChange={() => setCourse(2)} />
<label className="custom-control-label" htmlFor="radMultipleTimes">Multiple Times</label>
</div>
</div>
</div>
{/*PATTERN*/}
<label className="custom-control-label mt-3" htmlFor="customRadio1">Running Pattern</label>
<div className="col-md-1">
<label className="form-check-label" htmlFor="flexSwitchCheckDefault">Every</label>
</div>
<div className="col-md-2">
<input className="form-control" type="number" value={every} onChange={e => setEvery(Number(e.target.value))} />
</div>
<div className="col-md-4">
<select className="form-control" id="everyCount" onChange={e=>setPatternMode(Number(e.target.value))}>
<option value="1">Day</option>
<option value="2">Week</option>
<option value="3">Month</option>
<option value="4">Year</option>
</select>
</div>
{renderDaySelector()}
{/*TIME*/}
<label className="mt-3">Set Time</label>
<div className="form-check">
<div className="row">
<div className="col-12 col-sm-12">
<div className="form-check">
<input checked={timeMode === 1} className="form-check-input" type="radio" name="time" id="radAdapt" onChange={() => setTimeMode(1)} />
<label className="custom-control-label" htmlFor="radAdapt">Adapt Automaticaly</label>
</div>
<div className="form-check">
<input checked={timeMode === 2} className="form-check-input" type="radio" name="time" id="radSpecificDate" onChange={() => setTimeMode(2)} />
<label className="custom-control-label" htmlFor="radSpecificDate">Specific Time</label>
{timeMode === 2 && <TimePicker value={startTime} onChange={setStartTime} />}
</div>
</div>
</div>
</div>
</div >
<div className="card-footer pt-0 p-3 d-flex align-items-center">
<div className="w-60"> <p className="text-sm"> </p>
</div>
<div className="w-40 text-end">
<a className="btn bg-gradient-primary mb-0 text-end" onClick={SaveData}>Save Details</a>
</div>
</div>
</>
);
}
这是日历组件
export const Calender = ({name, dates}) => {
const [upcomingDates, setUpcomingDates] = useState([])
useEffect(() =>
{
let dts = [];
for (let i = 0; i < dates.length; i++) {
dts.push(
{ title: name, date: dates[i], className: 'bg-gradient-dark' }
);
}
setUpcomingDates(dts);
}, []);
return (
<div className="card card-calendar">
<div className="card-body p-3">
<FullCalendar
allDayClassNames="calendar"
plugins={[dayGridPlugin]}
initialView="dayGridMonth"
weekends={false}
events={upcomingDates}
/>
</div>
</div>
); }
这是发生的所有事情以及它不起作用的原因:
第一,当您将状态依赖项添加到 useEffect 时,它会陷入无限循环,因为它调用的函数会修改该状态。
组件在状态改变时肯定会重新渲染,效果如您所料,具有空依赖数组的 useEffect
仅在挂载和卸载时调用。
如果它使用空数组重新渲染,很可能是因为 pattern.UpcomingDates
是一个空数组,而不是因为组件没有按预期运行。您是否尝试记录 pattern.UpcomingDates
是什么?
除此之外,其他人已经注意到您的组件中出现了一些反模式内容。您应该在效果内部声明该函数,或者使用 useCallback
创建它并将其添加到 useEffect
.
我发现了问题。
而不是使用 "upcomingDates" 作为父组件 useEffect() 的依赖项
我不得不把它放在子组件(Calender 组件)中,这样它会在日期更改时重新呈现
export const Calender = ({name, dates}) => {
const [upcomingDates, setUpcomingDates] = useState([])
useEffect(() =>
{
let dts = [];
for (let i = 0; i < dates.length; i++) {
dts.push(
{ title: name, date: dates[i], className: 'bg-gradient-dark' }
);
}
setUpcomingDates(dts);
}, [dates]);
return (
<div className="card card-calendar">
<div className="card-body p-3">
<FullCalendar
allDayClassNames="calendar"
plugins={[dayGridPlugin]}
initialView="dayGridMonth"
weekends={false}
events={upcomingDates}
/>
</div>
</div>
);
}