Как вызвать событие handleChange изнутри функции с помощью jquery и отреагировать?

Я использую плагин fullcalendar-scheduler для следующего календаря. В настоящее время я интегрировал его с реакцией и рельсами. Чтобы изменить положение элемента, я вызвал функцию выбора из функции viewRender fullCalendar вместо рендеринга при реакции. В этом случае, как нам изменить состояние при изменении параметра выбора и снова получить данные из 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;

введите описание изображения здесь


person Code father    schedule 15.04.2019    source источник


Ответы (2)


Здесь ваш обратный вызов 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
  }

После этого вы сможете вызвать метод setState из функции handleChange.

person Sazedul Islam Sazid    schedule 15.04.2019
comment
Привет, @Sazedul. Я попытался внести изменения, как вы упомянули. Но он не запускает событие handleChange при добавлении в него debugger. Я что-то здесь пропустил? - person Code father; 15.04.2019
comment
Прости! Виноват. Я точно не называл handleChange из onChange. Только в привязке к контексту. Звоните так context.handleChange.bind(context)(this); Я немного обновил код. - person Sazedul Islam Sazid; 15.04.2019
comment
Спасибо. Это сработало. Просто быстрый вопрос. Есть ли способ после входа в функцию handleChange и установки состояния заставить componentDidMount () снова получить необходимые данные с обновленным состоянием? - person Code father; 16.04.2019
comment
Нет. ComponentDidMount () не будет вызываться снова, если компонент не перемонтируется. Если вам нужно снова получить эти данные, вы должны сделать это в функции handleChange или в функции componentDidUpdate () очень осторожно с определенным условием, чтобы оно не попало в бесконечный цикл. :) - person Sazedul Islam Sazid; 16.04.2019

Вы должны столкнуться с проблемой со ссылкой 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);
          ...
        });
      },
        ...
    });
}

благодаря.

person Mukesh Tivari    schedule 15.04.2019