Closed guibas741 closed 4 years ago
That looks like a bug. Please post your <CalendarStrip>
code or a Snack.
I tested markedDates
in the example app in this repo and it's working as expected in scrollable and non-scrollable modes. Are you dynamically updating the prop as the weeks change?
@peacechen sorry about the late response.
This is my code:
const WeekCalendarStrip = ({ selectedDate, onWeekChanged, onDateSelected, activities }) => {
const formattedDaysOfMonth = useMemo(
() =>
activities.reduce((acc, activity) => {
const [activityDate, activityItems] = activity;
const dots = activityItems.reduce((accDots, item) => {
const hasDot = accDots.find(dotItem => dotItem.key === DOTS_STYLES[item.type].key);
if (!hasDot) {
accDots.push({
key: DOTS_STYLES[item.type].key,
color: theme.colors[DOTS_STYLES[item.type].color],
selectedColor: theme.colors[DOTS_STYLES[item.type].selectedColor]
});
}
return accDots;
}, []);
acc.push({ date: activityDate, dots });
return acc;
}, []),
[activities, theme.colors]
);
return (
<CalendarStrip
showMonth={false}
locale={CALENDAR_STRIP_LOCALE[locale]}
leftSelector={<ChangeWeekIcon name="chevron-left" />}
rightSelector={<ChangeWeekIcon name="chevron-right" />}
customDatesStyles={customSundayStyle}
daySelectionAnimation={daySelectedStyle}
highlightDateNameStyle={calendarStripStyle}
highlightDateNumberStyle={calendarStripStyle}
markedDates={formattedDaysOfMonth}
onDateSelected={onDateSelected}
onWeekChanged={onWeekChanged}
selectedDate={selectedDate}
/>
);
}
I receive onDateSelected
, onWeekChanged
and selectedDate
as props to my component
This looks like an issue with useMemo
. As you may know, it optimizes by caching results when the inputs are the same.
Try to move the anonymous function outside of useMemo and pass it directly to CalendarStrip. If that works, debug why useMemo isn't returning the proper value when the week changes. I suspect you may need to add a variable to the useMemo watch list (the line with [activities, theme.colors]
).
I removed the useMemo but still occurs the same problem, the dots only appears when I select a day. If I set a static dots array It works, so I think the problem is that when the dots array changes is not rerendering the object.
In fact even when I change dynamically the selectedDate
prop the component doesnt rerender, I always need to click on the date so the style is applied.
So I found a solution that works for me.
I was using the CalendarStrip
as a header to a list like this. The WeekCalendarStrip
was my custom CalendarStrip
component:
<>
<WeekCalendarStrip
onDateSelected={handleDateSelected}
selectedDate={selectedDate}
activities={filteredActivities}
onWeekChanged={handleWeekChange}
loading={loading}
/>
<FlatList
ref={listRef}
data={filteredActivities}
keyExtractor={item => item[0]}
renderItem={renderItem}
initialNumToRender={7}
maxToRenderPerBatch={7}
ListEmptyComponent={renderEmptyState}
onScrollToIndexFailed={() => {}}
/>
</>
Since I wanted to be fixed on the top of the list I render the CalendarStrip
as a ListHeader and add the prop stickyHeaderIndices={[0]}
to set the Calendar fixed on top.
<FlatList
ref={listRef}
data={filteredActivities}
keyExtractor={item => item[0]}
ListHeaderComponent={() => (
<WeekCalendarStrip
onDateSelected={handleDateSelected}
selectedDate={selectedDate}
activities={filteredActivities}
onWeekChanged={handleWeekChange}
loading={loading}
/>
)}
renderItem={renderItem}
stickyHeaderIndices={[0]}
initialNumToRender={7}
maxToRenderPerBatch={7}
ListEmptyComponent={renderEmptyState}
onScrollToIndexFailed={() => {}}
/>
This way everytime the data prop changes the component will rerender and works just as expected. Besides that couldn't find any other way.
Maybe was not a bug and I wasn't using the lib the correct way, idk. Thanks for the attention and help @peacechen
@guibas741 Glad that you found a work-around for the issue. I'm attempting to recreate the issue in the sample app to debug.
I adapted the example with useMemo
and it renders the marked dates properly when switching weeks. There must be a difference in the way that the data is updated as the week changes.
Note that the activities
prop passed to the memo watch list will not trigger a recalculation if the array is the same instance, even if the values within it changes. filteredActivities
should be created fresh any time it changes.
2.0.2 adds support for callbacks to the markedDates
prop. An example usage is in the Readme:
https://github.com/BugiDev/react-native-calendar-strip#markeddatesformat-example
A callback may be a better fit for useMemo
.
This issue is still reproduced even when markedDates
is a fn.
When we pass markedDates
as a function, rerender will not be caused. The Strip
component checks markedDates immediately after I click the left
or right
button.
But my flow is like this:
left
buttonmarkedDates
markedDates
come from serveruseMemo
hook (or create a new markedDates
fn)I tried to use @guibas741 solution. In my case I tried to render FlatList with empty body.
<FlatList
data={markedDates}
ListHeaderComponent={() => (
<Strip
startingDate={currentWeek.startOf('week')}
selectedDate={selectedDate}
onDateSelected={changeSelectedDate}
style={styles.calendar}
calendarHeaderStyle={styles.headingText}
dateNumberStyle={styles.dateNumberStyle}
dateNameStyle={{ color: Colors.Primary }}
highlightDateNumberStyle={{ color: Colors.White }}
highlightDateNameStyle={{ color: Colors.White }}
weekendDateNameStyle={{ color: Colors.White }}
calendarAnimation={null}
daySelectionAnimation={daySelectionAnimation}
customDatesStyles={customDatesStyles}
markedDates={markedDates}
onWeekChanged={handleWeekChange}
/>
)}
stickyHeaderIndices={[0]}
renderItem={() => null}
/>
It rerenders when data
changes. But it also rerenders when I select a date.
It is not elegant, but it covers my needs for now.
I found a better solution. We should call updateWeekView
manually when got required markedDates
:
const ref = useRef<{ updateWeekView: (date: Moment) => void }>(null);
useEffect(() => {
if (markedDates && markedDates?.length > 0) {
ref.current?.updateWeekView(currentWeek);
}
}, [currentWeek, markedDates]);
return (
<Strip
ref={ref}
startingDate={moment().startOf('week')}
selectedDate={selectedDate}
onDateSelected={changeSelectedDate}
markedDates={markedDates}
onWeekChanged={handleWeekChange}
/>
);
@AMaklakov I couldn't recreate the bug with useMemo
in the example app. Would you try adapting your code in there? It's inside the example
folder in this repo.
Another option would be to recreate it in a Snack.
I'm glad that you found a work around using the updateWeekView
method. It would be better to fix any issues within the library so that work around isn't needed.
Edit: I can see how the bug could manifest if markedDates data changes after the week changes while it's passed as a function. Aside from updateWeekView
, another way to address that is to get the previous and next week's marked dates data when showing the current week. That way the markedDates data is already available when the left/right button is pressed.
Another way to deal with this is to pass markedDates
as an array. CalendarStrip detects when the markedDates
prop changes and updates itself.
Has this issue been fixed yet? I have tried all the above solutions and none of them solve the issue. Marked dates only show up after I press on any of the days in the calendar strip exactly the same as @guibas741. Is the solution as simple as re-rendering the component internally whenever marked dates are changed? This issue needs to be reopened since no solution is yet found. @peacechen Calendar strip does not detect when the marked dates prop changes. Only when pressing on a single day in the visible week triggers the re-render to show the marked dates. Also why does the onWeekChanged method invoked when pressing a day? I think it should only trigger onWeekChanged when the week changes by pressing the right and left buttons, futhermore, It should automatically re-render when marked dates change. Which is the expected behaviour.
Edit: For anyone who comes across the issue and reading this right now. set scrollable={true}, this results in the expected behavior until the issue is fixed for non scrollable calendar strip.
markedDates
along with other relevant props should be added to the prop detection list. Please submit a PR to fix that.
This fix has been published in 2.1.5.
I have an application that marks days according to the response of the api, but every time I change the week and call the api again I need to select a day so the dots appears on the screen.
Can someone help me? I tried to set the selected as the first day of the next week but couldn't do that either.