When building components that contain forms, we can use the component's internal state to track the value of those form fields. Let's create an example. First, we'll define a render method that returns some JSX.
We use div as a wrapper. Then we'll put an input field, type of text, and we'll set value to an empty string for now. Then below, let's create a log of this component's internal state. We'll use JSON.stringify, pass in this.state, null as the second parameter, and two as the third parameter, which will give us some nice spacing.
We run this and see we have an empty object on our state and a form field. Now, let's recreate that type of example where you type here, and the state gets updated. To do that, we'll need to create some initial state. We'll have to put a constructor, take some props, and call super with those props.
We set the state inside the constructor to have an empty text property start with. Then in the render method, we can use that value here. We can say this.state.text. Then we need to update this value every time the input changes, so we'll say oninput, and then we'll provide a method from this class.
We'll see setText. Now, we need to create this, so we can place it here. This will get the regular DOM event, and then we can update the internal state of the component by calling setState, providing the key text. Then we'll say event.target our value.
We'll need to bind this method in the constructor to ensure that every time it's called, it has the correct context. With that, we should be good to go. Hit save. Now, you can see that on page load, we have this text property on our state that is currently empty. If we type in here, you can see it's updating.
Now, using the internal state of a component to track the value of a form field is extremely important, as it means the component is the source of truth. When you come to submit a form, for example, you can just ask the component for the value from the text field, and you don't have to try and reach into the DOM and read it manually.
Now, when we save that, you can see that the text field is still being updated. Then only when I hit enter, do we get a form submission, and we get that value that was tracked internally. Now, for something so common as handling forms and inputs, this is a lot of boilerplate.
We have this method here that's sole purpose is just setting a value on the state. We have to set an initial state here, and we have to bind this extra method. This is where Preact's linkState comes in. It can remove much of this boilerplate for us, and handle these kinds of common use cases automatically.
To use it, we'll get rid of this setText method altogether. We won't bother setting any initial state. We'll delete all of this, and then we'll come down to the render method. Remember, in Preact, we gets props and state here.
We can just destructure the text property from the state, and we can actually set a default value for it here, right in the function signature. We'll change the value on the input to just use text. Then we can change this to a call to linkState.
The first argument is this component itself, because behind the scenes, linkState will call setState for us. It needs to know on which component it should call that. Then we provide as a string the name of the property on the state object that we want to update. In our case, it's text.
This function is provided as a separate module, so we can install it with Yarn. Yarn add linkState. Next, we need to import it, so we'll type import linkState from "linkstate." We have the same result as before, but we were able to remove a lot of boilerplate from our code, and we get a performance benefit from linkState, as it will only ever create a handler for this combination once.
Now, one final note. If you're building the kind of app where you're not actually submitting a form, but rather you're just accepting some input from the user, you can take this even further. Remove the form altogether. Remove the submit method. Remove that. All of a sudden, you don't need the constructor anymore, and there we have it.