My app has a calendar component that displays content for each day as you swipe horizontally (similar to the iOS Calendar app). I implemented this with a FlatList
which renders a DayContent
component for each day.
Each DayContent
component has its own local state populated on render by reading from the database for the specified date. For simplicities sake, let’s say each DayContent
component displays a counter and a button that takes you to a new screen EditCount
to edit the counter (this needs to be a separate screen since in my application the edit is more complicated than increment/decrement).
Here is the problem:
I first thought about implementing this EditCount
screen by passing count
and setCount
in the router params. However, I noticed that React Navigation passes router params as copies and not by reference, so when I call setCount
in the EditCount
screen, the count
is not updated in the EditCount
screen although it is updated back in the DayContent
component.
I then considered storing the count
and setCount
variables in my central state management system (Context API). However, I am unsure how this would work since each day in the calendar needs its own count
and setCount
.
Ideally, I don’t want to update the database in EditCount
, so if there are any other methods I would appreciate them.
I have been struggling with this seemingly straightforward problem for a few days now, please let me know if you have any ideas.
EDIT
The data
I pass to my FlatList
is an Array of length 7. The way I have it set up is that if I swipe to the edges of the array, (ie first, second, or second last, last index in the array) I recreate the array centred around the current day to create the illusion of an infinite list.
So every time I recreate this array of length 7, I assume it re-renders all the days and makes an API call to each one (ie 7 times on each re-render). Not sure if this is the most efficient method.
I am using Firebase Firestore to make my database requests, I haven’t looked too deeply into how the caching mechanism works, haven’t done any explicit caching though.
const DayContent = ({ dateStr }) => {
const [count, setCount] = useState();
useEffect(() => {
const dbContent = getContentFromDate(dateStr);
setCount(dbContent);
}, []);
const handleEdit = () => {
navigation.navigate("EditCount", { count, setCount });
};
return (
<View>
<TouchableOpacity onPress={handleEdit}>
<Text>Edit</Text>
</TouchableOpacity>
{count}
</View>
);
};
const EditCount = () => {
const { count, setCount } = useRoute().params;
return (
<View>
<TouchableOpacity onPress={navigation.goBack}>
<Text>Go back</Text>
</TouchableOpacity>=
<TouchableOpacity onPress={setCount((prev) => prev + 1)}>
<Text>Increment</Text>
</TouchableOpacity>
<TouchableOpacity onPress={setCount((prev) => prev - 1)}>
<Text>Decrement</Text>
</TouchableOpacity>
{count} /////// Does not update
</View>
);
};
2
Answers
I think you can update the
EditCount
screen’scount
params value separately too, by runningin addition to the original state update.
But I think this is not the best approach because you will have 2 separate states, one in
EditCount
and another one in theDayContent
.For a better solution I think you could use a global state management library (I have used Valtio for similar cases, but any other will work also) or context.
The general logic would be like this:
For doing this with Valtio, the code would look something like this
Set up the store:
Save the data to the store
Update the day data when it is changed
Use the day’s count from global state
For readability, performance and scalability, consider using
setParams
if it meets your needs.