2

I'm trying to code a date picker using ReactJS but I don't know how to create a dynamic table that displays the actual month days every time I hit previous or next buttons.

Here's my code:

// Next step I need to add a matrix of weekdays (1..31) and check each month existing days including leap year days
class DatePicker extends React.Component {
  constructor(props) {
    super(props);
    this.date = new Date();
    this.months = [
      "January",
      "February",
      "March",
      "April",
      "May",
      "June",
      "July",
      "August",
      "September",
      "October",
      "November",
      "December"
    ];
    this.days = [
      "sunday",
      "monday",
      "tuesday",
      "wednesdey",
      "thursday",
      "friday",
      "saturday"
    ]; // Should add keys for each item
    this.listWeekDays = this.days.map(day => (
      <li key={day}> {day.slice(0, 2)}</li>
    )); // su mo tu we th fr sa
    this.state = {
      month: this.date.getMonth(),
      year: this.date.getFullYear(),
      monthDays: [
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        10,
        11,
        12,
        13,
        14,
        15,
        16,
        17,
        18,
        19,
        20,
        21,
        22,
        23,
        24,
        25,
        26,
        27,
        28,
        29,
        30,
        31
      ]
    };

    this.month = () => this.getMonth(this.state.month);

    this.year = () => this.getYear(this.state.year);

    // ***************** days of month
    this.monthDays = [
      1,
      2,
      3,
      4,
      5,
      6,
      7,
      8,
      9,
      10,
      11,
      12,
      13,
      14,
      15,
      16,
      17,
      18,
      19,
      20,
      21,
      22,
      23,
      24,
      25,
      26,
      27,
      28,
      29,
      30,
      31
    ];

    /* ******************* EventHandling Binding *****************    */
    this.getNext = this.getNext.bind(this);
    this.getPrevious = this.getPrevious.bind(this);
    /* ***********************************************************    */
  }

  getPrevious() {
    if (this.state.month >= 1) {
      this.setState(prevState => ({ month: prevState.month - 1 }));
    } else {
      this.setState(prevState => ({ month: 11 }));
      this.setState(prevState => ({ year: prevState.year - 1 }));
    }
  }

  getNext() {
    if (this.state.month < 11) {
      this.setState(prevState => ({ month: prevState.month + 1 }));
    } else {
      this.setState(prevState => ({ month: 0 }));
      this.setState(prevState => ({ year: prevState.year + 1 }));
    }
  }

  getWeekDays() {
    return <li>{this.listWeekDays}</li>;
  }

  getFirstDay() {
    // console.log(typeof(this.month()));
    // 		console.log(typeof(this.year()));
    const year = this.year().toString();
    const month = this.state.month;
    const firstDay = new Date(year, month, "01");
    return firstDay.getDay();
  }

  getMonth(month) {
    switch (month) {
      case 0:
        return this.months[0];
      case 1:
        return this.months[1];
      case 2:
        return this.months[2];
      case 3:
        return this.months[3];
      case 4:
        return this.months[4];
      case 5:
        return this.months[5];
      case 6:
        return this.months[6];
      case 7:
        return this.months[7];
      case 8:
        return this.months[8];
      case 9:
        return this.months[9];
      case 10:
        return this.months[10];
      case 11:
        return this.months[11];
    }
  }

  getYear(year) {
    return year;
  }

  displayDays = (days, month, year, firstDay) => {
    let tr = document.createElement("tr");
    let td;
    let table = document.createElement("table");
    let body = document.getElementsByTagName("body")[0];
    let i = 0;
    let textNode;

    const emptyTds = () => {
      for (let j = 0; j < firstDay; j++) {
        days.unshift("");
      }
      return days;
    };
    const checkMonthDays = () => {
      if (month === 3 || month === 5 || month === 8 || month === 10) {
        days = days.splice(0, 30);
      } else if (month === 1) {
        // Check if leap year or not
        if (year % 4 === 0) {
          if (year % 100 === 0) {
            if (year % 400 === 0) {
              days = days.splice(0, 29);
            } else days = days.splice(0, 28);
          } else days = days.splice(0, 29);
        } else days = days.splice(0, 28);
      }
      return days;
    };
    const displayDaysTable = () => {
      days.forEach(day => {
        i++;
        td = document.createElement("td");
        textNode = document.createTextNode(day);

        td.appendChild(textNode);
        tr.appendChild(td);
        if (i % 7 === 0) {
          tr = document.createElement("tr");
        }
        table.appendChild(tr);
        body.appendChild(table);
      });
    };

    checkMonthDays();
    emptyTds();
    displayDaysTable();
  };

  render() {
    return (
      <div>
        <div className="ympicker-container">
          <div>
            <input
              type="button"
              className="month-button"
              value="<"
              onClick={this.getPrevious}
            />
          </div>
          <div className="monthyear-container">
            {this.month()} {this.year()}
          </div>
          <div>
            <input
              type="button"
              className="month-button"
              value=">"
              onClick={this.getNext}
            />
          </div>
        </div>
        <div className="week-days-container">
          <ul className="days-ul"> {this.listWeekDays} </ul>
        </div>
        <div>
          {this.getFirstDay()} //this is the first weekday of the month
        </div>
        <div>
          {this.displayDays(
            this.monthDays,
            this.state.month,
            this.state.year,
            this.firstDay
          )}
        </div>
      </div>
    );
  }
}

const DaysTable = () => {
  return <div />;
};

ReactDOM.render(<DatePicker />, app);
.ympicker-container {
	display: flex;
	border: 1px solid black;
	justify-content: space-between;
	align-items: center;
	background: #DCDCDC;
	width: 250px;
	font-size: 18px;
	font-weight: 600;
	padding: 0px 0;
	font-family: arial, sans serif;
}

.month-button {
	border: 0px solid blue;
	font-size: 16px;
	font-weight: 700;
	color: blue;
  height: 40px;
	cursor: pointer;
	outline: none;
	padding: 0 15px;
}

.month-button:hover {
  background: #191970;
	color: white;
}

.week-days-container  {
	display: flex;
	border: 1px solid black;
	border-top: 0;
	width: 250px;
	height: 25px;
	overflow:hidden;
}

.days-ul {
	display: flex;
	flex-direction: row;
	align-items: center;
  list-style: none;
	font-size: 20px;
	flex-grow:1;
	padding: 0;
	margin: 0;
}

li {
	display:flex;
  flex-grow: 1;
	align-items: center;  /* Center items */
 flex-direction:column;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="app"></div>

I have used createElement and appendChild but it adds new rows and cells to the table each time I hit the previous/next buttons.

How to create a table that updates existing rows/cells rather than creating new ones?

6
  • 2
    Have you looked into something like react-datepicker that might solve this difficult problem for you? Commented Jul 30, 2018 at 1:10
  • 1
    No I didn't. Since I am new to coding and particularly to ReactJS, I wanted to make this code as an exercise but I think it's helpful to take a look at this working application because it's not as easy as I thought it would be... Commented Jul 30, 2018 at 1:32
  • 1
    Append is not a bad option , but you have to empty the table first and then add new rows in order to get updated. Commented Jul 30, 2018 at 7:56
  • 1
    You should break this task into smaller pieces. For example, make a Month component. The end goal is to take a name prop or something similar to indicate what month it is and then generate a grid with numbers for the days, with the 1st starting on the correct day of the week. However, doing all that at once is difficult. So take a step back and make a grid with the numbers 1 through 30 and start on Sunday. Once you get that working, then add more functionality a little at a time. Commented Jul 30, 2018 at 7:58
  • Note that in React, you can do let table = <table></table> instead of using createElement(). Commented Jul 30, 2018 at 7:59

1 Answer 1

1

A couple of suggestions:

  1. Use JSX syntax for all elements. For example, you can do

    let table = <table/>;
    
  2. Similarly you can create <td> elements with map() instead of forEach():

    let tds = days.map(day => <td>day</td>);
    

    Now you need to split this list of days into the correct groups for each <tr>.

  3. Reduce getMonth() to a single line with a return:

    return this.months[month];
    
  4. Create a function named isLeapYear() to make this calculation more readable.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.