Sep 17, 2015
• 19 minute read
By Rand Seay
It can be tricky to handle elaborate form features such as repeatable sections and context-sensitive hiding or showing. Here is one approach using React.
React’s form documentation focuses on basic properties and outlines the differences between controlled and uncontrolled components within forms, but composing more advanced forms is left up to the imagination of the developer. What’s more is that React is all about one-way data flow down the component heirarchy, which can be challenging to reconcile in form management. There is form creation to deal with, as well as form submission. This article will focus on form creation, an will not address handling form submissions in React. The majority of one-off contact forms could be handled as a single component —<Form />, for instance.
But moving into the realm of a form framework is not so simple. It quickly becomes necessary to abstract away elements of the form into other React components. A natural way to break up a web form is into input groups —that is, a label and an element that takes input. This covers the majority of use cases that forms must handle, as evidenced by personal experience and the forms that can be built for Foundation or Bootstrap. Here is what a component <InputGroup /> could look like.
Notice the properties are passed in as simple values. This could be made simpler and more robust through the use of spread attributes and prop validation, but refining these examples into a framework is beyond the scope of this article.
The output is the same as Figure 1b, but now the <InputGroup /> component can be used repeatedly for other input groups.
Creating the <InputGroup /> component is a great first step, but it cannot yet handle textareas, selects, inline checkboxes, or inline radio buttons. It could be modified to handle these elements, or new React components could be used. The following example shows the functionality of a <select> element offloaded to a new component, called <Dropdown />, which accepts an array of options that render as the <select>’s options.
The output is below. Like many other snippets in this article, the <Dropdown /> component could be made more powerful with a few enhancements. Options, for instance could be contained in <optgroup>s, and carry information about other attributes, such as selected or disabled. The component would need to be adjusted to accommodate these.
Hiding and Showing
In the context of this example, selecting “Other” from the dropdown should reveal a new text input in which the user could specify a dietary consideration. Subscribing to the React line of thinking, this should be managed using the <Form /> component’s state. The change will be handled in a new method, handleChangeDiet, which is passed to <Dropdown /> as a property. Notice how the dropdown’s options are passed in as an array, and looped through in the render method. This can be enforced using prop validation and helps to separate concerns amongst components.
Another feasible expectation for this example form is a way to share it with multiple people, in this case it will be as few as one or as many as five. A repeatable section can be used to to meet this need, so it seems worthwhile to explore creating a new component —<Repeatable />. But handling this in React seems to require some interpretation regarding how state should be used in React. It is clear that state should include data used in triggering a UI update, but it should not include React components or duplicated data from the props.
State should contain data that a component’s event handlers may change to trigger a UI update.
Try to use props as the source of truth where possible. One valid use to store props in state is to be able to know its previous values, because props can change over time.
While reasoning about implementing a repeatable component, it becomes clear that keeping track of the UI that needs to be rendered would involve storing an array of the children elements. In this scenario that is any <InputGroup /> components (or other elements) that are passed to <Repeatable />. Adding elements to or removing elements from this array should trigger a UI update, making it stateful. This line of thinking works against the notion that state should not include other components or duplicated data from props. It isn’t cut and dry, but this is one way to implement a <Repeatable /> component.
The <Repeatable /> component could do with quite a few optimizations to make it less brittle, some of which may involve substantial refactoring. All of the components intended to be reused would benefit from enforcing the properties.
React is a powerful tool for building interfaces, but make sure you are using the right tool for the job. Functional and aesthetic consistency within a large number of forms may warrant a framework like what has begun to form here, but a vast number of forms can be handled on a case by case basis. React is enjoyable to play with, but can become unruly very easily. There are many ways to solve a problem, and the examples presented are one potential way.