javascriptreactjsloopsclasstoggle

How to toggle a single element from nested map iteration in react


I am new to react. I am working on a project to create shift rota for workers. Created a grid with cells. When you click on a cell it will change its value from day number to "Holiday" and if you click it again it will change back to day number. The problem is when I click a cell entire row of cells change their values. What to do to change the value only one cell ?

This is my code...

export default class DayNames extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isToggleOn: true
    };
  }

  handleClick = e => {
    this.setState(prevState => ({
      isToggleOn: { ...prevState.isToggleOn, [e]: !prevState.isToggleOn[e] }
    }));
  };

  render() {
    //Getting number of days of month
    const month = new Date().getMonth() + 1;
    const year = new Date().getFullYear();
    const numberDaysOfMonth = new Date(year, month, 0).getDate();

    let foo1 = [];
    for (let i = 1; i <= numberDaysOfMonth; i++) {
      foo1.push(i);
    }

    let foo2 = ["Ops1", "Ops2", "Ops3", "Ops4", "Ops5", "Ops6"];
    const ops = foo2.map((elem, index) => {
      return (
        <div className="ops_header" key={index}>
          {elem}
          {foo1.map((cell, i) => {
            return (
              <div
                className="operator"
                data-id={cell}
                key={i}
                onClick={() => this.handleClick(i)}
              >
                {this.state.isToggleOn[i] ? "Holiday" : cell}
              </div>
            );
          })}
        </div>
      );
    });

    return (
      <div>
        <div className="table">{ops}</div>
      </div>
    );
  }
}

This is link to sandbox .../components/DayNames/DayNames.js https://codesandbox.io/s/react-toggle-day-0rjop


Solution

  • Try this code sandbox click here

    Or see the below updated component.

    import React, { Component } from "react";
    import "./DayNames.css";
    
    export default class DayNames extends Component {
      foo2 = ["Ops1", "Ops2", "Ops3", "Ops4", "Ops5", "Ops6"];
    
      constructor(props) {
        super(props);
        const month = new Date().getMonth() + 1;
        const year = new Date().getFullYear();
    
        this.state = {
          bgColor: "lightslategrey",
          // isToggleOn: true,
          days: Array(this.foo2.length)
            .fill(0)
            .map(x => Array(new Date(year, month, 0).getDate()).fill(0))
        };
      }
    
      handleClick = (index, i) => {
        const { days } = this.state;
        days[index][i] = !days[index][i];
        this.setState({
          days
        });
      };
    
      render() {
        //Getting number of days of month
        const month = new Date().getMonth() + 1;
        const year = new Date().getFullYear();
        const numberDaysOfMonth = new Date(year, month, 0).getDate();
    
        let foo1 = [];
        for (let i = 1; i <= numberDaysOfMonth; i++) {
          foo1.push(i);
        }
    
        const ops = this.foo2.map((elem, index) => {
          return (
            <div className="ops_header" key={index}>
              {elem}
              {foo1.map((cell, i) => {
                return (
                  <div
                    className="operator"
                    data-id={cell}
                    key={i}
                    onClick={() => this.handleClick(index, i)}
                  >
                    {this.state.days[index][i] ? "Holiday" : cell}
                  </div>
                );
              })}
            </div>
          );
        });
    
        return (
          <div>
            <div className="table">{ops}</div>
          </div>
        );
      }
    }
    

    I have updated the code, the bug was that you were not keeping each and every cell state. I have fixed. Check days in the state.