0

I've recently been learning React with NextJs and ChakraUI. I've come across a problem that took me days to figure out a workaround.

So, I have this component:

import { 
    Button,
    Box,
} from "@chakra-ui/react"

const sort_data = (availableTimes,blockedTimes) => {

    // WARN: availableTimes && blockedTimes are ordered!

    var ordered_times_array = []
    for (let i = 0, j = 0 ; i < availableTimes.length || j < blockedTimes.length ;) {
        const a = availableTimes[i];
        const b = blockedTimes[j];


        if(j >= blockedTimes.length){
            const array_left = availableTimes.splice(i)
            array_left.map((time) => ordered_times_array.push({available: true, time: time}) )
            break;
        }
        if(i >= availableTimes.length){
            const array_left = availableTimes.splice(j)
            array_left.map((time) => ordered_times_array.push({available: false, time: time}) )
            break;
        }
        else if(a < b){
            ordered_times_array.push({available: true, time: a})
            i++;
        }

        else{
            ordered_times_array.push({available: false, time: b})
            j++;
        }
    }

    return ordered_times_array;
}

const create_buttons_left = (leftColArray) => {

    return leftColArray.map((value,index) => (
        
            value.available
            ? <Button w="75%" h={75} key={"schedule_button_left"+index} colorScheme="blue" > {value.time} </Button>
            : <Button w="75%" h={75} key={"schedule_button_left"+index} variant="outline" colorScheme="blue" > {value.time} </Button>

    ))
}

const create_buttons_right = (rightColArray) => {

    return rightColArray.map((value,index) => (
        
            value.available
            ? <Button w="75%" h={75} key={"schedule_button_right"+index} colorScheme="blue" > {value.time} </Button>
            : <Button w="75%" h={75} key={"schedule_button_right"+index} variant="outline" colorScheme="blue" > {value.time} </Button>

    ))
}

export const TimesGrid = ( { availableTimes, blockedTimes } ) => {

    console.log(availableTimes)
    console.log(JSON.parse(JSON.stringify(availableTimes)))

    // DEEP COPY
    //const times = sort_data(JSON.parse(JSON.stringify(availableTimes)),JSON.parse(JSON.stringify(blockedTimes)))

    const times = sort_data(availableTimes,blockedTimes)


    const leftCol = times.slice(0,Math.floor(times.length/2))
    const rightCol = times.slice(Math.floor(times.length/2),times.length)


    return (
    <Box display='flex' flexDirection='row' alignItems='center' justifyContent='space-evenly' w="100%" h="100%"  p={1}>
        <Box display='flex' flexDirection='column' gridRowGap={5} alignItems='center' justifyContent='center' w="100%" p={1}>
            {create_buttons_left(leftCol)}
        </Box>
        <Box display='flex' flexDirection='column' gridRowGap={5} alignItems='center' justifyContent='center' w="100%" p={1}>
            {create_buttons_right(rightCol)}
        </Box>
    </Box>
)}

This component is called by a parent component like this:

<TimesGrid availableTimes={[9,10,12,13,15,16,17]} blockedTimes={[8,11,14]} />

The availableTimes and blockedTimes are supposed to be in a state and come from an API, but to discard any problems relating to state or the API I manually placed an example of an expected input there.

The point of the TimesGrid component is to dynamically create Buttons based on the available and blocked times given in props, its also supposed to create a different type of Button based on the fact that the times are available or blocked. In order to more easily do this I created the function sort_data that will take the two availableTimes and blockedTimes arrays and make an object array that follows this pattern:

[{available:false,time:8},{available:true,time:9},{available:true,time:10},{available:false,time:11}]

I also want the times to be split into 2 columns which after having the "times" array (created as described above) is easily done by splitting the array in two. Lastly i use a map in each of this arrays to create the Buttons as you can see in the create_buttons_left and create_buttons_right functions.

The expected result will be as follows: expected result

But the result I get is weirdly as follows: actual result

After days of trying to figure this out I've found a solution, if I make a deep copy of the props I will get the expected result. The problem is I've no idea why I need to make a deep copy of the props to get the expected result, neither why the actual result is what it is, is this a React Bug ? Or is this expected? I was hoping someone could help me out. Thank you.

Astro
  • 3
  • 2
  • By making a deep copy, you are avoiding mutating the props objects. See the linked duplicate. – Matt Morgan Sep 22 '21 at 15:50
  • 1
    Hello Matt, yes it did help answer my question, I've figured out that I was using the Array.splice() instead of the Array.slice() inside my sort_data function. Turns out that Array.splice() is modifying the original array given in the props and that is why this problem was happening. Your help was really helpful, thank you. – Astro Sep 22 '21 at 16:03

0 Answers0