【问题标题】:How to trigger handleChange event from inside the function using jquery and react?如何使用jquery从函数内部触发handleChange事件并做出反应?
【发布时间】:2019-04-15 05:03:35
【问题描述】:

我正在使用 fullcalendar-scheduler 插件来跟踪日历。目前我已经将它与 react 和 rails 集成。为了改变元素的位置,我从 fullCalendar 的 viewRender 函数内部调用了 select 函数,而不是在 react 上渲染。在这种情况下,如何更改选择选项时如何更改状态并从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;

【问题讨论】:

    标签: reactjs fullcalendar fullcalendar-scheduler


    【解决方案1】:

    这里你的 onchange 回调没有反应组件上下文,所以你不能在没有访问正确上下文的情况下更改状态。我可能很快建议的一种解决方案是更改您的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方法了。

    【讨论】:

    • 嘿@Sazedul。我尝试按照您提到的进行更改。但是在其中添加debugger 时不会触发handleChange 事件。我错过了什么吗?
    • 对不起!我的错。我并没有完全从 onChange 调用 handleChange。仅与上下文绑定。像这样调用context.handleChange.bind(context)(this);我已经更新了一点代码。
    • 谢谢。这行得通。只是一个快速的问题。进入handleChange函数并设置状态后有什么办法,我们可以让componentDidMount()再次获取所需的数据并更新状态吗?
    • 没有。除非组件重新挂载,否则不会再次调用 componentDidMount()。如果您需要再次获取这些数据,那么您必须在一定条件下非常小心地在 handleChange 函数或 componentDidUpdate() 函数中执行此操作,以免陷入无限循环。 :)
    【解决方案2】:

    您一定遇到了this 引用的问题,因为您正在尝试访问与component this 关联的方法handleChange,但您使用的是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);
              ...
            });
          },
            ...
        });
    }
    

    谢谢。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-27
      • 2019-05-09
      • 1970-01-01
      • 1970-01-01
      • 2011-03-31
      • 2014-08-23
      • 1970-01-01
      • 2022-07-11
      相关资源
      最近更新 更多