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.