ERROR Function components cannot have string refs. We recommend using useRef() instead. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref at coerceRef (http://localhost:3000/static/js/bundle.js:59386:21) at createChild (http://localhost:3000/static/js/bundle.js:59620:32) at reconcileChildrenArray

This a React Calendar Application that should handle full CRUD operation with localforage to have a persist data upon refresh.

I cannot do my CRUD Operation as the Details Modal is not showing up upon click anywhere in the Calendar or even on the existing event.

my CalendarInfo.js

import React, { useState, useEffect, useRef } from "react";
import moment from "moment";
import { Calendar, momentLocalizer, Views } from "react-big-calendar";
import "react-big-calendar/lib/css/react-big-calendar.css";
// import { GetInitialEvents } from "../reducer";
import Details from "./Details";
import localForage from "localforage";

import { useStateValue } from "../StateProvider";

const localizer = momentLocalizer(moment);
let allViews = Object.keys(Views).map((k) => Views[k]);
console.log(allViews, "MISSION");

const CalendarInfo = () => {
  const [{ allEvents }, dispatch] = useStateValue();

  const [showModal, setShowModal] = useState(false);
  const [eventType, setEventType] = useState("add");
  const [newIndex, setNewIndex] = useState(0);
  const [eventInfo, setEventInfo] = useState({});

  useEffect(() => {
    var allEvents = [
      {
        id: 0,
        title: "Hello!",
        allDay: true,
        start: new Date(moment()),
        end: new Date(moment()),
        hexColor: "black",
        notes: "Have a great day!",
      },
    ];
    localForage.getItem("AllEvents", function (err, allEve) {
      if (allEve) {
        allEvents = allEve;
      } else {
        localForage.setItem("AllEvents", allEvents);
      }
    });
    dispatch({ type: "ALL_EVENTS", allEvents });
  }, [dispatch]);

  const handleHide = () => {
    setShowModal(false);
  };

  const handleShow = (slotInfo, type) => {
    const currentIndex = allEvents.length;
    setShowModal(true);
    setEventType(type);
    setEventInfo(slotInfo);
    setNewIndex(currentIndex);
  };

  const deleteEvent = (id) => {
    dispatch({
      type: "REMOVE_EVENT",
      payload: id,
    });
    setShowModal(false);
  };

  const addEvent = (obj) => {
    dispatch({
      type: "ADD_EVENT",
      payload: obj,
    });
    setShowModal(false);
  };

  const updateEvent = (obj) => {
    dispatch({
      type: "UPDATE_EVENT",
      payload: {
        id: obj.id,
        obj: obj,
      },
    });
    setShowModal(false);
  };

  const eventStyle = (event, start, end, isSelected) => {
    const bgColor = event.hexColor ? event.hexColor : "#265985";
    const style = {
      backgroundColor: bgColor,
      borderRadius: "5px",
      opacity: 1,
      color: "white",
      border: "0px",
      display: "block",
    };
    return {
      style: style,
    };
  };

  return (
    <div className="bodyContainer">
      <div className="well well-sm">
        <h3 className="instruction">Instructions</h3>
        <strong>To add an event: </strong> Click on the day you want to add an
        event or drag up to the day you want to add the event for multiple day
        event! <br />
        <strong>To update and delete an event:</strong> Click on the event you
        wish to update or delete!
      </div>
      <Details
        modalShow={showModal}
        handleHide={handleHide}
        eventType={eventType}
        eventInfo={eventInfo}
        newIndex={newIndex}
        deleteEvent={deleteEvent}
        addEvent={addEvent}
        updateEvent={updateEvent}
      />
      <Calendar
        localizer={localizer}
        selectable
        events={allEvents}
        views={allViews}
        step={60}
        showMultiDayTimes
        defaultDate={new Date(moment())}
        onSelectEvent={(event) => handleShow(event, "edit")}
        onSelectSlot={(slotInfo) => handleShow(slotInfo, "add")}
        style={{ minHeight: "500px" }}
        eventPropGetter={eventStyle}
      />
    </div>
  );
};

export default CalendarInfo;

my Details.js

import React, { useState, useEffect } from "react";
import { Modal, Button } from "react-bootstrap";
import moment from "moment";
import Datetime from "react-datetime";
import "../css/datetime.css";
// var Datetime = require('react-datetime');
const Details = ({
  modalShow,
  handleHide,
  eventType,
  eventInfo,
  newIndex,
  deleteEvent,
  addEvent,
  updateEvent,
}) => {
  const [showModal, setShowModal] = useState(modalShow);
  const [eventDetail, setEventDetail] = useState({
    id: eventType === "add" ? newIndex : eventInfo.id,
    title: eventInfo && eventInfo.title ? eventInfo.title : "",
    start: new Date(eventInfo && eventInfo.start ? eventInfo.start : moment()),
    end: new Date(eventInfo && eventInfo.end ? eventInfo.end : moment()),
    allDay: eventInfo.allDay ? true : false,
    hexColor: "#265985",
    notes: eventInfo.notes ? eventInfo.notes : "",
  });

  useEffect(() => {
    setShowModal(modalShow);
    setEventDetail({
      id: eventType === "add" ? newIndex : eventInfo.id,
      title: eventInfo && eventInfo.title ? eventInfo.title : "",
      start: new Date(
        eventInfo && eventInfo.start ? eventInfo.start : moment()
      ),
      end: new Date(eventInfo && eventInfo.end ? eventInfo.end : moment()),
      allDay: eventInfo.allDay ? true : false,
      hexColor: eventInfo.hexColor ? eventInfo.hexColor : "#265985",
      notes: eventInfo.notes ? eventInfo.notes : "",
    });
  }, [eventInfo, eventType, modalShow, newIndex]);
  const changeHandler = (e, ref) => {
    var eventDetail = eventDetail;
    let val = "";
    if (ref !== "allDay") {
      if (ref === "start" || ref === "end") {
        val = new Date(moment(e));
      } else {
        val = e.target.value;
      }
    } else {
      val = e.target.checked;
    }

    eventDetail[ref] = val;
    setEventDetail(eventDetail);
  };

  return (
    <Modal show={showModal} onHide={handleHide}>
      <Modal.Header closeButton>
        <Modal.Title id="contained-modal-title">Event Details</Modal.Title>
      </Modal.Header>
      <Modal.Body>
        <label> Event Name </label>
        <input
          type="text"
          className="form-control"
          placeholder="Enter the Event Name"
          ref="title"
          value={eventDetail.title}
          onChange={(e) => changeHandler(e, "title")}
        />

        <label> Start Date </label>
        {eventDetail.allDay ? (
          <Datetime
            value={eventDetail.start}
            dateFormat="MM-DD-YYYY"
            timeFormat={false}
            onChange={(e) => changeHandler(e, "start")}
          />
        ) : (
          <Datetime
            value={eventDetail.start}
            onChange={(e) => changeHandler(e, "start")}
          />
        )}

        <label> End Date </label>
        {eventDetail.allDay ? (
          <Datetime
            value={eventDetail.end}
            dateFormat="MM-DD-YYYY"
            timeFormat={false}
            onChange={(e) => changeHandler(e, "end")}
          />
        ) : (
          <Datetime
            value={eventDetail.end}
            onChange={(e) => changeHandler(e, "end")}
          />
        )}

        <label> Event Notes </label>
        <textarea
          className="form-control"
          placeholder="Event Notes"
          ref="notes"
          value={eventDetail.notes}
          onChange={(e) => changeHandler(e, "notes")}
        />

        <label> Event Color </label>
        <input
          type="color"
          value={eventDetail.hexColor}
          onChange={(e) => changeHandler(e, "hexColor")}
          style={{ marginRight: "20px", marginLeft: "5px" }}
        />

        <input
          type="checkBox"
          name="all_Day"
          value={eventDetail.id}
          checked={eventDetail.allDay}
          onChange={(e) => changeHandler(e, "allDay")}
        />
        <label> All Day </label>
      </Modal.Body>
      <Modal.Footer>
        {eventType === "add" ? (
          <Button bsStyle="success" onClick={() => addEvent(eventDetail)}>
            Add
          </Button>
        ) : (
          <Button bsStyle="warning" onClick={() => updateEvent(eventDetail)}>
            Update
          </Button>
        )}
        {eventType === "add" ? null : (
          <Button bsStyle="danger" onClick={() => deleteEvent(eventDetail.id)}>
            Delete
          </Button>
        )}
        <Button onClick={handleHide}>Close</Button>
      </Modal.Footer>
    </Modal>
  );
};

export default Details;

my reducer.js

// import moment from "moment";
import localForage from "localforage";

export const initialState = {
  allEvents: [],
};

const reducer = (state = initialState, action = {}) => {
  switch (action.type) {
    case "ALL_EVENTS":
      return { ...state, allEvents: action.allEvents };
    case "REMOVE_EVENT":
      var newState = state;
      newState.allEvents = newState.allEvents.filter(function (obj) {
        return obj.id !== action.payload;
      });
      localForage.setItem("AllEvents", newState.allEvents);
      return newState;
    case "ADD_EVENT":
      var newState2 = state;
      newState2.allEvents.push(action.payload);
      localForage.setItem("AllEvents", newState2.allEvents);
      return newState2;
    case "UPDATE_EVENT":
      var newState3 = state;
      newState3.allEvents[action.payload.id] = action.payload.obj;
      localForage.setItem("AllEvents", newState3.allEvents);
      return newState3;
    default:
      return state;
  }
};

export default reducer;

I was expecting to add, update or delete an Event, but the Details model is not working, and not even giving an error!

1

There are 1 best solutions below

0
On

The Details component is using invalid React ref prop values, specifically ref="title" on the input element and ref="notes" on the textarea element.

<input
  type="text"
  className="form-control"
  placeholder="Enter the Event Name"
  ref="title" // <-- invalid ref value
  value={eventDetail.title}
  onChange={(e) => changeHandler(e, "title")}
/>

...

<textarea
  className="form-control"
  placeholder="Event Notes"
  ref="notes" // <-- invalid ref value
  value={eventDetail.notes}
  onChange={(e) => changeHandler(e, "notes")}
/>

These should both be removed.

<input
  type="text"
  className="form-control"
  placeholder="Enter the Event Name"
  value={eventDetail.title}
  onChange={(e) => changeHandler(e, "title")}
/>

...

<textarea
  className="form-control"
  placeholder="Event Notes"
  value={eventDetail.notes}
  onChange={(e) => changeHandler(e, "notes")}
/>

localForage.getItem is asynchronous and the useEffect callback in CalendarInfo doesn't wait for it to fetch stored data before it dispatches the "ALL_EVENTS" action to "initialize" the allEvents state value.

Update the effect to only dispatch the "ALL_EVENTS" action once the stored state has been retrieved.

const storeAllEvents = allEvents => ({
  type: "ALL_EVENTS",
  allEvents
});
useEffect(() => {
  localForage.getItem("AllEvents")
    .then(storedAllEvents => {
      let allEvents = [
        {
          id: 0,
          title: "Hello!",
          allDay: true,
          start: new Date(moment()),
          end: new Date(moment()),
          hexColor: "black",
          notes: "Have a great day!",
        },
      ];

      if (storedAllEvents) {
        allEvents = storedAllEvents;
      } else {
        localForage.setItem("AllEvents", allEvents);
      }

      dispatch(storeAllEvents(allEvents));
    })
    .catch(err => {
      // handle error or ignore
    });
}, [dispatch]);