react-big-calendar 从远程 url redux 问题中获取事件

react-big-calendar fetch events from remote url redux issue

我在尝试制作演示预约系统时遇到了一个奇怪的情况。 我想通过 Django 后端 rest api 获取事件,它工作得很好,我也可以在控制台中看到数据。

这是我的索引文件(提供商所在的位置): https://hastebin.com/ubefumofor.js

我的日历渲染组件: https://hastebin.com/oronefibap.scala

我的 Redux,我正在使用它的 reduxsauce 库: https://hastebin.com/usetavasub.js

我的 ReduxSaga 文件: https://hastebin.com/wutuvelefi.js

现在,问题是,我在获取事件后努力做一些事情:

现在我知道 props 不能修改,而 state 可以。

有什么错误的建议吗?谢谢

注:本人刚接触react和redux相关技术


解决方案


componentDidUpdate(prevProps, prevState){

if(prevProps.events !== this.props.events) {

     let a = this.state.events.slice();
     let p = this.props.events
     for(var i = 0; i < p.length; i++ ){
         a[a.length] = p[i]

     }
     this.setState({events: a})



   }
  }

这应该可以解决问题,但下面 Nagaraj 的回答也是一个非常好的解决方案。

我已经更改了你的 js 文件中的一些内容:

  1. 总是使用setState函数来改变状态,不要直接改变状态元素,像this.state.events.push(..是直接更新状态,这是错误的。
  2. 使用componentWillReceiveProps检查,如果收到的新道具(nextProps)与旧道具(this.props)不同,则只有setState。通常,当触发 fetchEvent 操作时,将事件设置为未定义,当您收到响应时,将其设置回 api 响应,在这种情况下您可以使用:

喜欢:

if (!this.props.events && nextProps.events) {
  this.setState({ events: nextProps.events });
}

日历文件:

import React from 'react';
import ReactDOM from 'react-dom';
import BigCalendar from 'react-big-calendar';
import events from './events';
import moment from 'moment';
import {
  ButtonGroup,
  Button,
  Modal,
  Form,
  FieldGroup,
  FormGroup,
  ControlLabel,
  FormControl,
  HelpBlock,
  Alert,
  OverlayTrigger,
  Popover
} from 'react-bootstrap';
import DateTime from 'react-datetime';
import {
  connect
} from 'react-redux';
import AppointmentActions from './Redux/AppointmentRedux';
import EventComponent from './Components/EventComponent';
import Halogen = require('halogen');


BigCalendar.setLocalizer( BigCalendar.momentLocalizer(moment) );

class Calendar extends React.Component {
  props: {
    handleAddCity: () => void,
    bookAppointmentForDoctor: () => void,
    fetchEvents: () => void,
    events: null
  }

  constructor(props) {
    super(props);
    this.state = {
      date: new Date(),
      events: props.events,
      fromDate: null,
      toDate: new Date(),
      forValue: 15,
      showAddFormModal: false,
      hours: 12,
      minutes: 20,
      enabled: true,
      showNameError: '',
      showFromDateError: ''
    };
    console.log(this.props)
  }

  handleSelect(info) {
    console.log(info.start.toLocaleString())
  }

  onClick() {
    // Create a copy of the object before you change the state
    let events = this.state.events.slice();
    events.push({
      'title': 'some Party',
      'start': new Date(2017, 3, 15, 7, 0, 0).toLocaleString(),
      'end': new Date(2017, 3, 16, 10, 30, 0).toLocaleString()
    });
    this.setState({ events });
  }

  open() {
    this.setState({ showAddFormModal: true });
  }

  close() {
    this.setState({ showAddFormModal: false });
  }

  handleFromDateTimeChange(newDate) {
    return this.setState({ fromDate: newDate });
  }

  handleToDateTimeChange(newDate) {
    return this.setState({ toDate: newDate });
  }

  handleEventSelect(event, _this) {
    return (<Popover id="popover-positioned-right" title="Popover right">
        <strong>Holy guacamole!</strong> Check this info.
      </Popover>);
  }

  handleMinutesSelect(_this) {
    this.setState({ forValue: _this.target.value });
  }

  handleAddSubmitButton() {
    var flag = true
    const nameValue = ReactDOM.findDOMNode(this.refs.name).value;
    if (nameValue === '') {
      this.setState({ showNameError: "Please input a name. " });
      flag = false;
    }

    if (this.state.fromDate === null) {
      this.setState({ showFromDateError: 'Please input right date and time' });
      flag = false;
    }

    const fromDate = new Date(this.state.fromDate).toLocaleString();
    const toDate = moment(fromDate).add(this.state.forValue, 'm');
    const newToDate = new Date(toDate).toLocaleString();

    console.log(nameValue, this.state.forValue, fromDate, newToDate);

    // Create a copy of the object before you change the state
    let events = this.state.events.slice();
    events.push({
      'title': nameValue,
      'start': new Date(fromDate),
      'end': new Date(newToDate),
      'hexColor': '#FF5722',
    });

    if (flag) {
      this.props.bookAppointmentForDoctor(3, nameValue, new Date(fromDate), new Date(newToDate))
      this.setState({ showAddFormModal: false });
    }
  }

  eventStyleGetter(event, start, end, isSelected) {
    var backgroundColor = event.hexColor;

    var style = {
      backgroundColor: backgroundColor,
      borderRadius: '0px',
      opacity: 0.8,
      color: 'black',
      border: '0px',
      display: 'block'
    };

    return { style: style };
  }

  componentDidMount() {
    this.props.fetchEvents(1);
  }

  componentWillReceiveProps(nextProps) {
    let oldEvents = this.props.events;
    let newEvents = nextProps.events;

    // Check here if only newEvents is different than oldEvents and update state.
    // Like always set events as undefined if ajax request starts and set it back
    // Once you receive the data
    // if (newEvents <---> oldEvents ??? ) {
      this.setState({ events });
    // }
  }

  render() {
    let { hours, minutes, enabled } = this.state;

    if (this.props.fetching) {
      return (<h1>loading.....</h1>)
    }

    if (this.state.events !== null) {

      return (
        <div style={{height: 580}}>
          <h3 className="callout">Book appointment and events here.</h3>
          <BigCalendar
            selectable
            popup
            events={this.state.events}
            height={500}
            defaultView='month'
            scrollToTime={new Date(1970, 1, 1, 6)}
            defaultDate={new Date()}
            onSelectEvent={(event, e) => this.handleEventSelect(event, e)}
            onSelectSlot={(slotInfo) => this.handleSelect(slotInfo)}
            eventPropGetter={(event) => this.eventStyleGetter(event)}
            components={{
                toolbar: this.CustomToolbar,
                event: EventComponent,
              }} 
            />

          <Button
            bsStyle="primary"
            bsSize="small"
            onClick={this.open.bind(this)}>
            Add appointment
          </Button>

          <Modal show={this.state.showAddFormModal} onHide={this.close.bind(this)}>
              <Modal.Header closeButton>
                  <Modal.Title>Add Appointment</Modal.Title>
              </Modal.Header>
                  <Modal.Body>
                    <form>
                         <FormGroup bsSize="large">
                            <FormControl type="text" placeholder="Enter Name" ref="name"/>{this.state.showNameError}
                          </FormGroup>
                       <FormGroup>
                          <ControlLabel>From (Date and Time)</ControlLabel>
                          <DateTime onChange={this.handleFromDateTimeChange.bind(this)}/>
                          { this.state.showFromDateError }
                          <HelpBlock>Please choose Date and Time of from when you want the appointment from,
                            both could be done using the same widget.</HelpBlock>
                        </FormGroup>
                       <FormGroup controlId="formControlsSelect">
                           <ControlLabel>For a period of</ControlLabel>
                           <FormControl componentClass="select" onChange={this.handleMinutesSelect.bind(this)} value={this.state.forValue}>
                              <option value="15">15 minutes</option>
                              <option value="30">30 minutes</option>
                              <option value="45">45 minutes</option>
                              <option value="60">60 minutes</option>
                              <option value="90">90 minutes</option>
                              <option value="120">2 hours</option>
                           </FormControl>
                       </FormGroup>

                      <Button type="button" onClick={this.handleAddSubmitButton.bind(this)}>
                          Submit
                      </Button>
                    </form>
              </Modal.Body>
          </Modal>
        </div>
      )
    }

    return (<h1>loading....</h1>);
  }
}


const mapStateToProps = (state) => {
  return {
    city: state.appointment.city,
    events: state.appointment.events,
    fetching: state.appointment.fetching,
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    handleAddCity: (city) => dispatch(AppointmentActions.setUserCity(city)),
    bookAppointmentForDoctor: (doctor_id, customer_name, from_date, to_date) =>
      dispatch(AppointmentActions.bookAppointmentForDoctor(doctor_id, customer_name, from_date, to_date)),
    fetchEvents: (doctor_id) =>
      dispatch(AppointmentActions.fetchEventsByDoctor(doctor_id))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Calendar)