So how else can we optimize this?
Well you might notice that this jsx code here in end can be repeated over and over again, every input
looks like this.
We have a view, we have a label with the text input itself and we might also want some error text which we
can display.
So it would make sense to outsource this into a separate component
and that's exactly what I want to do.
I'll go to my components folder and there in the UI folder, I'll add an input.js file which will hold
a React component, so I import react from react of course and then import a couple of things from React
Native and these things in the end are my view, text component, the text input component and the stylesheet
because I'll need this as well from React Native and with that here, we can create the input component
here which gets props and needs to return some jsx. We'll have a styles object with Stylesheet.create
and of course in the end, we export this as a default here, this component.
Now if we go back to the edit product screen, we can cut this title jsx code, so the surrounding view,
the text, the text input and this error message part, cut it out of here, out of the edit product screen
and move that into the input screen and there, use it as a return value in the input component here.
Now of course, we'll need to adjust this a little bit but that's our general skeleton which I want to use
there.
Now also from the edit product screen, I'll take the styling, there
this form control label and input styling can all be removed and should be moved over into the input
component and there into the stylesheet.
So now with that of course, this only can be used for a title and I want to create an input component
which can be used for any input, that for example means that the label here needs to be dynamic,
so I'll expect to get this on a prop named label maybe.
I also in general want to make sure that the text input can be configured from outside,
so I will forward all props I get on my input component here to the text input, so that on this input
component, we'll be able to set things like the keyboard type because I don't want to hardcode this
in here,
instead this should be a reusable wrapper around the text input component which can be configured from
outside.
So these settings here for example, I will cut them from here so that we can set them from outside,
I'll also get rid of these listeners because we don't really need them.
The error text here should also be settable from outside, so I will check for props
error text here
and of course you can as always give these props any names you want,
of course we'll also have to change what we assign here as a value and so on,
that will also change.
But now we can already start using that input here,
let's go to the edit product screen and there, I will just go to the top and import input from the components
folder of course and there from the UI folder and there from input
and then we can start using the input component in the place where I previously had my text input component
here.
So there, I will just add input and now on input, we can add the setting which I cut, like the keyboard
type and so on because that will be forwarded.
We can set our title label, like title and also our possible error text like Please enter a valid title,
though that's not all we'll do regarding validation but it's a start.
This is how we can then use this input
and of course this is also how we can now use it to replace the other inputs.
So for example here, the imageUrl, here I set this to imageUrl, Please enter a valid imageUrl
maybe, keyboard type default is okay, auto capitalization auto correction is not required, return
key type
next we can leave that, we're not doing anything with it, we could add some logic but I'm not doing it
here. For the price I'll leave the conditional rendering but in there, I can add my input here,
set this to price please enter a valid price, keyboard type here should of course not be default but
decimal pad, don't need auto capitalization or auto correction so let's get rid of that. And for the description
here, I'll also add my input here, description, please enter a valid description, keyboard type can be default,
I can use auto capitalization, auto correction, however I'll not set a return key type but here I want
to set multiline just like this which allows for multiline editing and set number of lines to let's
say three.
This however is only used by Android but there, it will restrict the number of lines you can enter, on
iOS, this will not have an effect but this is how you can now use this input component and still configure
it from outside.
That being said, as I said we're not done though,
what about the value and this change text listener?
There are different ways of how you could handle this text input or the text values here.
You could still simply forward it to the parent component and only use this input component as a visual
wrapper
but I will take it a bit further and I want to manage this value and the validity of this single input
inside of the input component,
so that I then only report back to my parent component, so to the edit product screen in the end what
the current value is and whether it's valid or not
so that validation doesn't need to happen inside of the parent component
but again that is totally up to you whether you want to do that or not. Here
I will add a new constant in the input component, text change handler where I get this enter text because
that's now what I bind or what is already bound and I will generally keep it like this, though we don't
need the identifier here anymore,
don't need bind therefore because this is inside of a single input, we don't need an identifier
there.
This will now therefore trigger on every keystroke and hence
now here, I want to check whether this is valid or not and also manage my value of that input. Now for
that, we can again use a reducer or individual states managed with use state or created with use state
but I'll go for a reducer
and here I have my input reducer, name is up to you, gets the state, gets an action and returns a new state
in the end.
Then here into the input, I can call use reducer and forward or set this to the input reducer
and my initial state for this input here will be a simple Javascript object with three values. The value
which you currently have, which initially can be empty but actually I want to allow myself to set an
initial value from outside because we will need this,
for example when we're editing products, so I check whether props
initial value is set,
again this is of course a name you can choose however you want
and if this is the case, I'll set this to props
initial value,
otherwise I'll set out to set this to an empty string,
then I will also manage whether this is valid or not and there again I can check if props initially valid
let's say is set.
If that's the case, then I will automatically take that value which should be true or false,
if it's not the case, this will automatically be treated as false
and I will also manage whether this has been touched yet or not,
so whether the user did actually type here and this can be useful for helping us with providing a better
user experience regarding when we show validation errors
and here I will set this to false initially.
Now of course, this will return us the input state snapshot and a dispatch function which I here can
also name dispatch because we will have no name clash and in the text change handler, of course I will
dispatch an action, an action which I therefore will create here with an action identifier, input change
for example, that should be dispatched here,
type input change and then here in the reducer, we can add a switch statement to mix things up, check the
action type and for the case input change, we want to do something. In the default case, I will just return
my state, so I will not do anything but here of course, I want to do something else.
Now here in the text change handler, obviously I dispatch input change and I want to forward the value
which I stored in a value key
but now here, I also want to do input specific validation and forward the information whether the input
is valid or not. And for this, attached
you find some validation setup or some validation code which I'll now walk you through.
Here I have a constant that sets up an email regular expression, so a regular expression that allows
us to validate email addresses,
we have no email address input yet but later when we add authentication, we will have that and then I
want to be able to use this input component,
so then this component should be prepared to also validate emails.
Then I have the is valid helper variable here because then we have a bunch of checks here.
Now I want to make sure that we can set the criteria we want to use from outside with the help of props,
so for example I check if a required props is set, if that's set to true,
so if it's set at all and if that is the case, then this check will be performed. If the required prop
is not set, this check will not be performed and therefore this validation is not performed and that's
the case for all my different validations here.
So we can set a required prop to check whether it's not empty, if it is empty, we set is valid to false.
We can set an email prop on the input component to validate it for being a valid email address and
if it's not, we set it to false.
We can check a number for being too small or too big or a string for being too short with the min length
validator
and of course you could add more logic here, possibly also with a package like ValidateJs which
I already mentioned earlier. Now with that, I will have is valid in the end which is true by default but
false as soon as one validation fails and therefore now, we can add is valid to this dispatched action
as well.