Let's start with the favorites.
I want to make sure that when I'm in a recipe, I can click that star and that adds this as a favorite
and shows it here in the favorite screen where I currently see nothing because I removed these dummy favorites.
For that
I'll start in the store folder, in the actions folder and add a new folder which I'll also name meals.js,
you can name it differently if that's confusing to you but yes, these two files meals.js here
in actions and meals.js in reducers and in the actions meals.js
file, I will set up some Redux actions which typically starts by setting up some unique identifiers
which I store in constants and that will be toggle favorites.
You can use whatever you want here,
the value you store here typically is the same identifier,
so this is the identifier value and that is simply stored in a constant so that we don't have to manually
type this later but we can always import this constant and avoid
typos.
Again if it's brand new to you,
definitely check out a dedicated React Redux resource first.
Now I'll also use the action creator pattern here
which means I have that identifier here but now I will also export a function that creates me an action
because an action is not just an identifier often and in this case that's the case,
it also needs some extra payload and therefore we actually have an action which is an object with identifier
and payload and therefore here, I'll add a function that creates me such an action object and I'll name this
toggle favorite like this,
select the identifier but in camel case and this is a function that can receive some data because I will
be the one calling it and that should return a Javascript object which describes this action, so which
has let's say a type,
that's typically how you call that identifier field but you're generally free to use whatever you want
there
and in my case, that will be toggle favorite,
so I point at this identifier, I use this string as an identifier and then any extra data you want,
for example here I'll need to add the meal ID of the meal which should be toggled and that means
that this function also needs to get this ID an an input because otherwise I can't store it in the action
which I created.
So this is my action creator for the toggle favorite action, now in the reducer,
I want to act when I get such a toggle favorite action and for this, I'll use a pattern which you probably
have seen
if you worked with Redux before, I'll use a switch statement here and switch on my action type because
keep in mind that action will be an object with a type property which identifies the action that occurred.
So I switch on that action type and then I handle different cases for the different actions I might
have.
So here I have the toggle favorite action and for this, you need to import toggle favorite from actions meals
and that's why I'm storing this in the separate constant because now I don't have to manually type toggle
favorites where I might have a typo in there,
instead I just use that constant in which the string is stored. And now here, I run whichever logic I
want to run to then work with that action and change my state such that the meal with the meal ID
that's part of my action is added to favorite meals or removed from there if it already was part because
it's a toggle action here,
I want to add items that aren't favorites yet, I want to remove items that are. Side note, in the switch
statement,
I also will add a default case where I just return the unchanged state.
We shouldn't really reach that but in case we do, well then that's what we do
but back to the toggle favorite case. In there,
I now want to in the end find out whether my meal with the ID that's part of the action because I'm
adding the meal ID to the action,
if that meal is already part of favorite meals,
if yes I want to remove it,
if it's not, I want to add it. So for this, I'll start by finding the index of the meal
in the favorite meals array because if that is then -1, I know I didn't find an index so it wasn't
part,
if that's greater or equal than zero, I know that the item was part of that. So I'll have my existing
index here and I get this by using state favorite meals and there I entered an equals sign and state
is simply my current state snapshot which I get and there, we'll have a favorite meals property and I
can use the find index method, a normal Javascript method which I can use on arrays to get the index
of an item that matches a certain criteria which I set up here in a function I pass to find index,
this function runs on every meal in the array and if it returns true, then it gives me the index of the
element for which it ran through.
So here I want to return true if meal.id is equal to action.mealId. That simply means that
the meal I'm looking at in my favorite meals has the same idea as the meal for which this action occurs
and that means if that is true for any item in favorite meals, that the meal we're trying to toggle is
already part of the favorites, so we'll have to remove it because this will now return us the index of
the item,
therefore existing index will be greater than -1 and therefore, we can then use that index to
remove it.
If this returns true for no item,
so if this in the end returns -1, I know that the item isn't part of favorite meals yet. So existing
index holds very important information and hence I will use it here in an if statement and check if existing
index is greater or equal than 0, which means we already have that meal as part of the favorite meals
or not.
Now in the if case, so the item already is part of the favorite meals, I want to remove it.
So here, I will in the end return a new state because in your reducer, in the end, you have to return
a new state which is a new object where I first of all copy the existing state with the spread operator
here to
pull out all the key-value pairs of the existing state, so that I don't lose any state and don't
override any state
I don't want to override and then I will only override the favorite meals, so I'll add this property
again to override the existing favorite meals with my new favorite meals which should in the end be the
old favorite meals without the item on this index.
And now here to get my updated favorite meals, I'll create a new constant, updatedFavMeals, there I will
first of all create a new array and populate it with my existing meals, so move my existing favorite meals
like this and I do this to create a copy of that array so that I don't manipulate the original array
if I now start working on that because now I want to take my updatedFavMeals, call splice here
and then remove the item at the existing index and only that item.
So this will in the end take this updatedFavMeals index, edit it and remove the item at this index
and edit this array,
that's why I copied it so that I don't edit the original array and now we can use this updatedFavMeals
array here and store this back as favorite meals into our store because that will be the favorite
meals array without the meal we just removed.
In the else case which is of course relevant if we're not finding a product so we want to add it, here
I also want to return a new state where I first of all copy the old state and then I will override
favorite meals to basically be my old favorite meals
and now I can call concat, a Javascript method built into Javascript which returns a new array
which takes the old array and adds a new item and there, I want to add the meal for that ID here
and of course I can find that meal on my state, there on meals, this is this property, state here always
refers just to this state structure here by the way
and now here, I can find the meal which I want to add
and since this gets very long, I'll actually outsource this into a new line,
so my meal which I want to add can be found with the find method which runs on all meals and there, if
mealId is equal to action.mealId, then I have the meal I want to add and it's this meal which
I concatenate to my favorite meals, so that I add this to that array. That should be logic that works
and that manages our favorites.
Now we need to make sure that we actually dispatch this action whenever we click that star icon in our
header.
To dispatch the action,
let's go to the meal detail screen because that's where we have our star
icon here and when I press this button, I in the end want dispatch an action.
Good news is dispatching actions is very easy,
we got use selector,
we also got use dispatch which in the end gives us an easy way of firing a function.
The bad news is that this of course can only be used in the component body here, in our functional component
and not in the navigation options.
So we'll again have to work with set params to communicate between navigation options and the component
but that is something we can of course do. So here in meal detail screen,
I'll simply start by calling use dispatch and what this does is it gives us a dispatch function, a function
we can call to dispatch new actions and I'll store in a constant named dispatch. Next, I want to create
a toggle favorite handler which is a function which in the end should dispatch this action.
So here I want to call dispatch which I have available thanks to this hook
and now to dispatch, I need to forward the action which I want to dispatch
and for this, I'll use this action creator toggle favorite here.
So we just have to import this, import
from the store folder, there from actions and there from the meals.js file, from there
I want to import toggle favorite, not the identifier in all caps but this action creator function. With
that imported here, in dispatch we can call toggle favorite like this and now here we need to forward the
ID and that of course is the mealId which we're already extracting up there
or selectedMeal.Id would also work, whatever you want,
I'll simply go for the mealId like this.
Now we can reactivate use effect to communicate to our header, not with this line though but instead
with props navigation set params and now here, I'll name this toggle fav, the name is up to you and
point at my toggle favorite handler.
Now toggle favorite handler should therefore be added as a dependency here and to avoid infinite loops,
I'll again use use callback which I import from React to wrap this function here.
So here use callback is wrapped around this function and we need to specify dependencies here
and dependency number one is dispatch,
React Redux will make sure that this never changes though, so that's a dependency which will never change,
dependency two is mealId,
that also shouldn't change whilst we're on this page,
it will always have the same value. So with that, this function shouldn't really be recreated which means
this effect will never rerun unless it's recreated which only is the case if we had a new ID, in which
case it would be good that this is recreated but otherwise, this won't change and hence we should have
a secure communication channel here.
Now in the header, we can now extract that handler, by the way
we also don't need the mealId here anymore,
we can now simply get the toggle favorite function out of our navigation data navigation get param and
then I name that toggle fav, so that's the param name which we can extract and this should give us
access to this function which we pass. Now toggle favorite is what should actually be executed when we
press this button.
So here, I will point at toggle favorite because this holds a pointer at this function and therefore
this function will be executed when we now press this star. Well we'll see if that works.
Let's save that and go back to the spaghetti here and click this star and click to favorites and that
looks good,
spaghetti are part of the favorites.
Let's click the star again here and go to favorites and it's gone,
that makes a lot of sense.
Click it again, it's back, go to the detail page here in the favorites tab and click on the star and
go back, well it's gone.
Let's also verify it on Android, for the schnitzel maybe, favorites is empty right now. Let's go back
and click the star, go to favorites,
here it is, go back, click the star and it's gone. So this logic generally works.
There are a couple of things I still want to work on, for example I want to change the icon based on
the favorite status,
I also want to show some text in the favorite screen when it's empty but generally, dispatching and
using the data works even if we need it in a header.