我正在使用fullcalendar-scheduler插件来跟踪日历。目前我已经将它与react和rails集成在一起。为了改变元素的位置,我从fullCalendar的viewRender函数内部调用了select函数,而不是render on react。在这种情况下,当select选项更改时我们如何更改状态并从api再次获取数据?
import React from "react";
import PropTypes from "prop-types";
import axios from "axios";
class TestCalendar extends React.Component {
constructor(props) {
super(props);
this.state = {
cars: [],
events: [],
price: [],
selectDates: [],
startDate: moment(),
endDate: moment().add(3, 'years')
}
}
componentDidMount() {
const headers = {
'Content-Type': 'application/json'
}
axios.get('/api/v1/test_calendars?date_from=' + this.state.startDate.format(), { headers: headers })
.then(res => {
const cars = res.data;
this.setState({ cars });
});
axios.get('/api/v1/test_calendars/events?date_from=' + this.state.startDate.format(), { headers: headers })
.then(res => {
const events = res.data;
this.setState({ events });
});
axios.get('/api/v1/test_calendars/prices?date_from=' + this.state.startDate.format(), { headers: headers })
.then(res => {
const price = res.data;
this.setState({ price });
});
this.updateEvents(this.props.hidePrice);
}
componentDidUpdate() {
console.log('componentDidUpdate');
this.updateEvents(this.props.hidePrice);
console.log(this.state.cars);
}
componentWillUnmount() {
$('#test_calendar').fullCalendar('destroy');
};
handleChange(e) {
debugger;
}
updateEvents(hidePrice) {
function monthSelectList() {
let select = '<div class="Select select-me"><select id="months-tab" class="Select-input">' +
'</select></div>'
return select
}
function getDates(startDate, stopDate) {
var dateArray = [];
while(startDate.format('YYYY-MM-DD') <= stopDate.format('YYYY-MM-DD')) {
dateArray.push(startDate.format('YYYY-MM'));
startDate = startDate.add(1, 'days');
};
return dateArray;
}
$('#test_calendar').fullCalendar('destroy');
$('#test_calendar').fullCalendar({
selectable: false,
defaultView: 'timelineEightDays',
defaultDate: this.props.defaultDate,
views: {
timelineEightDays: {
type: 'timeline',
duration: { days: 8 },
slotDuration: '24:00'
}
},
header: {
left: 'prev',
right: 'next'
},
viewRender: function(view, element) {
let uniqueDates;
$("span:contains('Cars')").empty().append(
monthSelectList()
);
$("#months-tab").on("change", function() {
let index, optionElement, month, year, goToDate;
index = this.selectedIndex;
optionElement = this.childNodes[index];
month = optionElement.getAttribute("data-month");
year = optionElement.getAttribute("data-year");
goToDate = moment([year, (month - 1), 1]).format("YYYY-MM-DD");
$("#test_calendar").fullCalendar('gotoDate', moment(goToDate));
$("#months-tab").find("option[data-month=" + month + "][data-year=" + year + "]").prop("selected", true);
this.handleChange.bind(this)
});
let dates = getDates(moment(), moment().add(3, "years"));
uniqueDates = [...new Set(dates)];
$('#months-tab option').remove();
$.each(uniqueDates, function(i, date) {
$('#months-tab').append($('<option>', {
value: i,
text: moment(date).format('MMMM') + " " + moment(date).format('YYYY'),
'data-month': moment(date).format('MM'),
'data-year': moment(date).format('YYYY'),
}));
});
},
resources: this.state.cars,
resourceRender: function(resourceObj, labelTds, bodyTds) {
labelTds.css('background-image', "url(" + resourceObj.header_image + ")");
labelTds.css('background-size', "160px 88px");
labelTds.css('background-repeat', "no-repeat");
labelTds.css("border-bottom", "1px solid");
labelTds.addClass('resource-render');
labelTds.children().children().addClass("car-name");
},
resourceLabelText: 'Cars',
dayClick: function(date, jsEvent, view, resource) {
},
dayRender: function(date, cell){
cell.addClass('dayrender');
},
select: function(startDate, endDate, jsEvent, view, resource) {
},
events: this.state.events.concat(this.state.price),
eventRender: function(event, element, view){
},
schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives'
});
// Should stay after full component is initialized to avoid fc-unselectable class on select tag for months
$("#months-tab").on("mousedown click", function(event){event.stopPropagation()});
$(".prev-link").on("click", function(event){event.stopPropagation()});
$(".next-link").on("click", function(event){event.stopPropagation()});
}
render () {
return (
<div id='test_calendar'>
</div>
);
}
}
export default TestCalendar;
这里你的onchange回调没有react组件上下文,因此你无法在不提供正确上下文访问权限的情况下更改状态。我可能很快建议的一个解决方案是改变你的updateEvents
函数,如下面的方法。我只保留了更改的代码。
updateEvents(hidePrice) {
let context = this;
... // your code
$('#test_calendar').fullCalendar({
... // your code
viewRender: function(view, element) {
... // your code
$("#months-tab").on("change", function() {
... // your code
// Call the handleChange with the context.
context.handleChange.bind(context)(this); // edited here
});
... // your code
});
... // your code
}
然后,您将能够从handleChange
函数调用setState方法。
您必须面对this
引用的问题,因为您正在尝试访问与handleChange
关联的方法component this
,但您使用viewRender
的正常函数而不是应该使用arrow function
看下面的更新代码,它将解决问题,
updateEvents(hidePrice) {
$('#test_calendar').fullCalendar({
...
viewRender: (view, element) => { // convert to arrow function so, this (component instance) will be accessible inside.
// store the reference of this (component instance).
const $this = this;
$("#months-tab").on("change", function (e) {
...
// always bind methods in constructor.
$this.handleChange(e);
...
});
},
...
});
}
谢谢。