2

I'm displaying 2 select dropdown's, one displaying a start hour, one displaying an end hour. Both states start_hours and end_hours uses data imported from the same file. I wanted so that when user selects a start hour, the end hour will only display hours AFTER the selected start hour. However, while trying to filter/disable the previous end_hours, the start_hours became affected as well.

The only way I was able to achieve my results is creating a duplicate file with the same data, give it a different import name, and setting it to the initial state. This is incredibly redundant and I am sure there is a cleaner way to do this.

times.js

export default [
    {
        "time": "07:05",
        "display": "07:05 AM",
        "disabled": false
    },
    {
        "time": "07:0",
        "display": "07:10 AM",
        "disabled": false
    },
    {
        "time": "07:15",
        "display": "07:15 AM",
        "disabled": false
    },


App.js:

import React from 'react';
import hours from './data/hours';
....
...
  constructor(props) {
    super(props);

    this.state = {
               start_hours: hours,
               end_hours: hours
          };
  }
.... 
.....

  onStartHourClick(e) {
       ....
       ....
       let endTime = this.state.end_hours, state = Object.assign({}, this.state);
        this.state.end_hours.map((time, index) =>{
          if(Date.parse(`${day} ${e.target.value}`) >= Date.parse(`${day} ${time.time}`)) {
            state.end_hours[index].disabled = true;
          }
        });
        this.setState({state});
  }

The code I wrote successfully filters/disables previous hours but it is ALSO doing it on the start_hours thus making both state objects identical. The only way I was able to achieve end_hours being filtered out ONLY was importing the SAME json file twice:

import hours from './data/hours';
import hours2 from './data/hours2';
...
...
  constructor(props) {
    super(props);

    this.state = {
               start_hours: hours,
               end_hours: hours2
          };
  }

  • Why `this.state = {start_hours: times , ... }` not `this.state = {start_hours: hours} ?? where is the times reference come from. maybe this is due to bad referencing? react state is immutable (mean it cannot be change in place) can only be change through set state. maybe try console log the state after each event – Hamuel Jul 07 '19 at 16:21
  • @Hamuel sorry! I corrected it, it is supposed to be `start_hours: hours`, I mistyped. And I did try to change through setState but it still isn't working correctly. My problem is that it is changing BOTH `start_hours` and `end_hours` when I only wanted it to change `end_hours` – Storm Parker Jul 07 '19 at 16:26
  • ok so did you try `{start_hours: JSON.parse(JSON.stringify(hours)), end_hours: JSON.parse(JSON.stringify(hours)),}` – Hamuel Jul 07 '19 at 16:28
  • @Hamuel this works! Can you explain the reason? – Storm Parker Jul 07 '19 at 16:32

1 Answers1

0

This is known as deep cloning

How do I correctly clone a JavaScript object?

Currently your code are referencing the same object when you set state

const a = {a: 1, b: 1}
const b = a
a['a'] = 2
b['a'] = 3

What is the output? some people might say it is 2 but actually it is 3 because it is referencing the same object, the object is not copy. The technique below is a quick and dirty way to clone an object in javascript

const a = {a: 1, b: 1}
const b = JSON.parse(JSON.stringify(a))
a['a'] = 2
b['a'] = 3

now console.log(a) will return 2 and console.log(b) will return 3 because it is not referencing the same object anymore

So your set state show be something like below to make sure you are not referencing the same object and make sure start_hours and end_hours is its own object both of them does not need to be deep copy only one of them need to be

{
 start_hours: JSON.parse(JSON.stringify(hours)), 
 end_hours: JSON.parse(JSON.stringify(hours))
}

Hamuel
  • 633
  • 5
  • 16