I'm not quite sure that the problem so I will explain it a bit wider.
I'm trying to make react schedule component, like on this image:
The problem I get is that I'm getting empty flex components with no elements in them.
So I have a JSON file with data. It looks like this:
export const eventHours = [
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,]
export const scheduleData = [
{
weekDay: {
title: 'Day 1',
day: 'Monday',
date: 'NOvemb 30th',
},
events: [
{
icon: '/images/icons/talk.png',
type: 'Lecture',
title: 'adsasd',
startTime: 11,
endTime: 14,
},
],
},
{
weekDay: {
title: 'Day 2',
day: 'Monday',
date: 'NOvemb 30th',
},
events: [
{
icon: '/images/icons/talk.png',
type: 'Lecture',
title: 'adsasd',
startTime: 11,
endTime: 14,
},
{
icon: '/images/icons/talk.png',
type: 'Lecture',
title: 'adsasd',
startTime: 16,
endTime: 18,
},
],
},]
So the idea I had is to map through days, for each day I render the Flex component (I'm using ChakraUI), then inside that component, I'm mapping through all events for that day. For each event, I'm mapping through hours. If the hour is less than event startTime I'm returning true if it's equal then return true with that I'm making a boolean array.
Finally, I'm mapping through that array on the true return empty box component making empty spaces, and on false return actual cell component.
Here is code to make it easy to understand
events component: I'm getting rendered two of them for 2 days from data
const Events = (props: Props) => {
return (
<>
<Box w='1000px' h='510px'>
{scheduleData.map((day) => <EventFlex events={day.events} />)}
</Box>
</>
)}
eventFlex component here is happening all logic
I'm not getting anything rendered, the parent div is empty.
interface Props {
events: Event[]
}
type Event = {
icon: string
type: string
title: string
startTime: number
endTime: number
}
const EventFlex = ({ events }: Props) => {
const [emptyEventCells, setEmptyEventCells] = useState<boolean[]>([])
const [myEvents, setEvents] = useState<Event[]>([])
useEffect(() => {
setEvents(events)
generateEmptyEventCells()
}, [])
const generateEmptyEventCells = () => {
let tempFlexItems: boolean[] = []
myEvents.map(event => {
eventHours.map(hour => {
if (hour < event.startTime) {
tempFlexItems.push(true)
}
if (hour === event.startTime) {
tempFlexItems.push(false)
}
})
})
setEmptyEventCells(tempFlexItems)
}
const calculateEventLength = (startTime: number, endTime: number) => {
return endTime - startTime;
};
return (<Flex h='102px'>
{emptyEventCells.map((isEmpty: boolean, index) => (
<React.Fragment>
{isEmpty ? <Box w="83px"></Box> : <EventCell
size={`${calculateEventLength(myEvents[index].startTime, myEvents[index].endTime) * 83}`}
icon={myEvents[index].icon}
type={myEvents[index].type}
title={myEvents[index].title}
time={`${myEvents[index].startTime}h-${myEvents[index].endTime}h`}
/>}
</React.Fragment>
))}
</Flex>);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Ternary operator is working as expected, but I found 2 errors and 1 issue with your code.
wrong usage of useEffect
. Since setEvents
is asynchronous, calling generateEmptyEventCells
after setEvents(events)
won't give expected result. Break it into 2 useEffects
Length of emptyEventCells
is not same as myEvents
, hence, in the EventFlex component you should not use index from emptyEventCells
as myEvents[index]
. If you want to know the event
related to isEmpty
value, better find another way like this.
Issue: If rendering elements from an array, like using Array.map, you should provide key
prop to the Element.
Final code is
interface Props {
events: Event[];
}
type Event = {
icon: string;
type: string;
title: string;
startTime: number;
endTime: number;
};
type CellInfo = {
isEmpty: boolean;
event: Event;
};
const EventFlex = ({ events }: Props) => {
const [emptyEventCells, setEmptyEventCells] = useState<CellInfo[]>([]);
const [myEvents, setEvents] = useState<Event[]>([]);
useEffect(() => {
setEvents(events);
}, []);
useEffect(() => {
generateEmptyEventCells();
}, [myEvents]);
const generateEmptyEventCells = () => {
let tempFlexItems: CellInfo[] = [];
myEvents.forEach((event) => {
// use forEach if not returning anything
eventHours.forEach((hour) => {
// use forEach if not returning anything
if (hour < event.startTime) {
tempFlexItems.push({ isEmpty: true, event });
}
if (hour === event.startTime) {
tempFlexItems.push({ isEmpty: false, event });
}
});
});
setEmptyEventCells(tempFlexItems);
};
const calculateEventLength = (startTime: number, endTime: number) => {
return endTime - startTime;
};
return (
<Flex h="102px">
{emptyEventCells.map(({ isEmpty, event }, index) => (
<React.Fragment key={`${index}`}>
{isEmpty ? (
<Box w="83px"></Box>
) : (
<EventCell
size={`${
calculateEventLength(event.startTime, event.endTime) * 83
}`}
icon={event.icon}
type={event.type}
title={event.title}
time={`${event.startTime}h-${event.endTime}h`}
/>
)}
</React.Fragment>
))}
</Flex>
);
};
const Events = (props: Props) => {
return (
<>
<Box w="1000px" h="510px">
{scheduleData.map((day, index) => (
<EventFlex events={day.events} key={`${index}`} /> // use key
))}
</Box>
</>
);
};
Here is the codesandbox demo: https://codesandbox.io/s/young-firefly-cxvbh?file=/src/App.tsx