So let's continue working on this and let's change this to a let's say text change handler function
which we now assigned to
onChange text here on the title but which we also can add to our imageUrl.
So here for onChange text, I also set this to text change handler and now for this to work, we need to
make sure that this function gets information about which input it was triggered for,
so whether you just typed in the title or in the imageUrl input.
So besides getting the text, I also expect to get a different argument,
I expect to get my input identifier or anything like that, you can name it whatever you want
so that this function can be reused for multiple text inputs and we still get information about which
input triggered this
so that we can forward this to our reducer where we will later need this information to update our state
correctly.
Now to get this input identifier functionality, we need to go to the places where we use text change
handler and there, we can for example use bind or use a wrapping anonymous arrow function instead to
bind this where I don't care about but more importantly, to configure the arguments this function will
get and there it's one special argument which I want to pass and that's the input identifier.
And for this function here, for this function call on this text input,
this of course would be title. For this input down there, we would have bound this imageUrl
and again this should be an identifier which you also have in your state up there, in your form state
which you pass to the reducer. With that added, we'll make sure that text change handler is executed and
gets this input identifier argument.
The text argument will also be received because that's the default argument which React Native would
pass us anyways and this will then automatically be received as a last argument in that function.
So we don't have to and we actually can't bind this here,
it will be forwarded automatically.
Now we have a reusable text change handler function here and we can assign this to the other inputs as well,
like here to the imageUrl, like down here for the price, we can bind text change handler, bind
this and then price is our identifier here and of course also for the description, there we
paste this in and then bind to this and the identifier is description.
Now this same handler can be used for all inputs but right now of course, it does one thing,
it validates each input for its length.
Now that might make sense and it does here,
no input should be empty but you might want some other validation as well,
for example my price here should be a number greater than zero
let's say, so that zero is not allowed as an input. That on the other hand is not some criteria that matters
to me on other inputs. We'll find an elegant solution for this later,
for now let's stick with this smallest common denominator validation which we can apply to all inputs
to make sure they're at least not empty
and let's focus on the reducer function and how our form state should change
when this form input update action with all that data is dispatched
because right now nothing will happen.
We have our initial state on the reducer there and that will be passed or stored in the form state but
this state will never change.
Well that's why we dispatch an action, right?
That's why we do this here,
this is why form input update is dispatched and that is why we want to write some code here in the
reducer when this action reaches us here.
Now in there, if this action reaches us, in the end the goal is to make sure that we update this state
snapshot,
our current state snapshot appropriately.
This means that for example, we'll need some updated values and for this, I create a new object because my
input values here,
that's an object and I will create a new one to replace it and I will first of all copy the existing
state input values.
Keep in mind that the state here is passed in automatically, that's our current state snapshot before
we update it and therefore initially, that's this state snapshot here for example and this will have an input
values key which in turn holds an object
and that's just what I'm copying here,
I'm copying all the key-value pairs of that input values snapshot and then I want to replace the key
value pair for the input for which this action was dispatched,
the good thing is that we attach the input identifier to the action. So we can go to the reducer and
dynamically override a key here in the copied input values,
the key is action.input of course, that's our input identifier which we attached to the action and
the value should be action.value,
that's all we need to do. So we dynamically store the value that was dispatched with the also dynamically
assigned input and update our copied input values.
Now we can return a new state here because that's the goal of your own reducer,
like for the Redux reducer, in the end it has to return a new state snapshot
and there I want to copy the existing state but override the input values with the updated values which
copies my old values so that no data is lost and then replaces one of the key-value pairs in there. Now obviously,
it's not all to just replace the values or update the values,
I also want to update my validities.
So here are my updated validities and just as with the values, I start by copying my input
validities key because the input validities is a key here
as you can see
and then I want to replace one validity for the input which we get on the action, so title, imageUrl
or whatever it is and replace this with action is valid because we get the information whether that
is valid or not as part of the action that is dispatched, right?
We're forwarding this here based on this basic validation we have here.
Now with that, we just need to update this here,
so our input validities here are equal to the updated validities and now we just have to manage the
overall form validity. For that,
I add a variable, a helper variable, form is valid which initially is true and then I loop through all
my updated validities
which are my old copied validities
and then the one updated validity for the input we just typed in.
So here, we have a for/in loop where we go through all the keys in updated validities and the goal is
simple, I check every form input validity and if all form input validities are valid,
if they are all true, then the overall form is valid.
If at least one of them is false, the overall form is false,
the overall form is not valid. So therefore here form is valid is equal to the latest form is valid state
we have and then the updated validities validity for the input w're currently looking at, so for this key
and we're going through all the inputs here.
The way this boolean operator works is such this false overrides true,
so if any input is false, form is valid will be set to false and can't be set to true thereafter.
So if at least one input is invalid, the overall form will be invalid and therefore now I don't need
to copy my state anymore here because I will replace the entire state snapshot and set form
is valid to my derived form is valid here
and of course to avoid confusion, we could also name this updated form is valid here and here and here
and here.
Now just one additional thing, if we don't make it into this if block,
so if some other action was dispatched or anything else, then I will just return the unchanged state
here. With that,
we added a reducer logic that is able to handle any input or any typing into any of our text inputs,
now we just need to use the form state which will change with every keystroke which is just what happened
before but now all centralized and merged into one managed state which I think is way cleaner than having
tons of different states for validities and values. And by the way, managing all these states manually and
separately would have become a big problem when you then want to validate the overall form validity.
So now we can use the form state which will change with every keystroke and we use it for example here
on the values. We pass back in the value for this text input by accessing form state input values.title.
Now obviously, we do the same here for the imageUrl, we feed this back in,
so just what we did before and now with our form state managed with the help of the reducer,
same for the price of course and also the same for the description, like that.
Now besides changing it here, we also have to change it in some other places,
for example here in the submit handler of course. There, title is valid,
this no longer exists but we can check form state input validities title, that is true or false and
if it's false, it means it's not valid,
so this check should be fine.
Of course however we care about the validity of all inputs,
so instead of just checking them all manually here by concatenating them into a long if statement, we can
just check if form state form is valid is false because that means some input is false and therefore
we show this error,
that's the shorter check and this is of course also when we want to avoid that the form gets submitted.
If the form gets submitted however, title description and imageUrl, that no longer exists,
instead that's now form state inputValues.title and so on,
so we do the same here
for the description and for the imageUrl. We derive our values from the form state, same of course
here for creating. We get the title and the description and the imageUrl and also of course the price
which I still convert to a number with the plus here, we get all of that out of our form state of
the input values. Therefore of course our dependency here is not title, description, imageUrl and so on,
instead it's just form state. If the form states changes, which it will on every keystroke, then this
function should be rebuilt and it needs to be rebuilt because the information that's used by the function
changes with every keystroke,
the validity can change with every keystroke,
the values you want to submit can change with every keystroke.
We also can of course now use this to show our error message. Instead of checking title is valid which
doesn't exist anymore,
we check formState.inputValidities.title, if that is false, then I want to show this text.
So now with that,
if we save all of that, we should be able to check this. If we go to our page here,
we see this error initially.
If I start typing, it disappears, if I
try to submit this, it fails because I have three empty inputs.
If I do enter something valid there on the other hand,
this works. If I try editing,
let's try this on Android maybe to mix things up.
So if I try editing the red shirt here, I can submit this just fine,
now let's also try submitting this if
this is empty, then I get the alert.
So this works in the way that it should work,
now with this more elegant form management using use reducer and of course you can not just use use reducer
when working with forms,
this is just a particularly good example for when it makes sense to merge multiple states together and
therefore have highly reusable and efficient code.
Now I'm still not done though because all this code repetition with the inputs here which are always
structured in the same way and the missing customization possibilities regarding the validation,
these are the things I want to tackle next.