Day 4 - June 30, 2022
Topics Covered
- Validating component properties
- Event handling
- Event pooling and synthetic events
Validating component properties
You can think of property validation in components similar to field validation in HTML forms. The goal is to inform developers implementing a component that they have provided a value that is not acceptable. This improves the experience for developers that are interacting with components you have created.
Type Validation:
The prop-types
package includes simple property type validators that can handle most
primitive types of JavaScript values. You will use these validators frequently to ensure property values
contain the expected data type. It is worth noting that if you do not pass a value to a given property,
the type is not validated and no warning is displayed.
Copy and paste the following code example into VS Code then try changing the property values so they use
the wrong types. For example, change line 45 to read myBool={"false"}
. You should see an
warning similar to: Failed prop type: Invalid prop `myBool` of type `string` supplied to
`MyComponent`, expected `boolean`.
Required Values:
As mentioned earlier, no warning is shown if you simply ignore a property but there are times when a
value is required. Using the prop-types
package, we can indicate the values that must be
specified. Let's modify the previous code example to require all properties be specified.
Any value:
Sometimes you don't know (or care) what type of property is passed to your component but you want to
ensure something is passed in. In otherwords, it is a required property but the data type doesn't
matter. This is easily accomplished using PropTypes.any.isRequired
.
Specific types:
If you need a property validator that checks for a custom type defined by your application, for example
a component called MyComponent
, there are PropTypes
for this as well.
-
PropTypes.instanceOf(MyComponent)
- requires an instance ofMyComponent
. -
PropTypes.oneOfType([PropTypes.string, MyComponent])
- requires an instance ofstring
orMyComponent
. -
PropTypes.arrayOf(PropTypes.instanceOf(MyComponent))
- requires an array ofMyComponent
instances.
Specific types:
So far all of the validation checked the type of property but the actual value may be just as important.
-
PropTypes.oneOf(possibleValues)
- Checks the property value and warns if the value is not contained in an arraypossibleValues
. -
PropTypes.shape(comparableObj)
- Similar toinstanceOf()
but we don't care about the actual type. Instead, we care that the object passed in has all of the required properties and that each property is valid.
Custom validators:
The validators provided by the prop-types
package cover a wide range of scenarios but you
may require some sort of custom validation logic. Simply define a function accepting thre arguments
(props, name, component) => { }
. Return null
if the value is valid otherwise
return an Error
instance.
Event Handling
An event handler is a callback function that is triggered once an event takes place.
For example, this could be a button click or when the value of a text box is changed. In React, we can
configure event handlers on components by modifying the JSX. Let's take a look at a simple example.
When the button is clicked, the status is changed from Waiting
to Clicked
.
You can see that an event handler is attached within the JSX by adding the apprropriate attribute.
In the previous example, we have added onPress={e => setStatus("Clicked")}
to the
<Button>
component. The handler is defined inline using arrow function syntax.
When you assign an event handler, React adds the handler to an internal dictionary of events and functions. Tracking events in this way allows React to keep the declarative UI structures separate from the platform we are running on. For example, if we are running our App in a browser, events are not attached to the underlying DOM element. Instead, there is a single event handler on the document. Once fired, an event bubbles up through the tree to the document. React sees the event, checks it's internal mapping, then runs the appropriate function. It may seem like alot of effort but the benefits are plentiful. Since we don't rely on the DOM (or the specifics of the OS), we can wire up events using the same method on all platforms.
As we just learned, we will define events directly in our JSX markup. React uses a declarative approach for setting up event handlers. An advantage to this approach is that event handlers in JSX markup are part of the UI structure. If you compare this to something like JQuery, in JQuery you write imperative code that selects the relevant DOM element and attaches event handler functions. You are left no choice but to track down code that assigns event handlers making debugging and understanding program flow more difficult.
In the code example above, the event handler was coded inline within the JSX using arrow function syntax. This is handy if your function is simple but there are times when you need to perform multiple opeations or add complex logic. In these cases, you can move the handler logic to a separate function, then call that function instead.
It is also possible to pass arguments to event handlers. In the following code example, we are
rendering a list of buttons using the objects contained in the players
array. When a
button is clicked, the text is replaced to include the name
of the player object associated
to the button that was clicked. This code example combines many of the topics we have learned so far.
Event Pooling
We discovered that events are bubbled up and handled by an internal mapping system. This allows for a more uniform apporach for handling events that can be shared across different browsers or devices. Even if we manually add new event listeners using JavaScript (rather than directly in the JSX), React will wrap event handlers to ensure event propagation works correctly. These wrapped events are called Sythentic events.
If all events, native or synthetic, are being handled by React, this could cause a bottleneck in performance. Furthermore, synthetic events will need to be cleaned up (garbage collected) at some point which can cause further performance issues.
- Garbage Collection
- A form of automatic memory management in which the program tries to free up memory space that is no longer being used. When the garbage collector is running, JavaScript code is paused adding potential delays.
React deals with this problem by creating a pool for handling synthetic events. Whenever an event is triggered, it uses a slot from the pool. when the event has completed, the slot is released and can be reused again later. This prevents garbage collection from running too frequently especially when alot of events are triggered in a short period of time.
There is a small problem that should be noted. If the synthetic event handler invokes some asyncronous code, the event will finish up rather quickly and get returned to the pool. When this happens, the properties associated to the event are cleared up. If the asyncronous function accesses any of these properties they will not be available.