Objective
Create a TrackedTextarea
component that tracks the length of characters entered into a <textarea>
element.
Description
We are all familiar with the <textarea>
element from HTML. It is used to represent a large input block where we can enter mere textual data.
The maxlength
attribute of a <textarea>
is used to specify the maximum number of characters that could be entered into it.
For example, the following <textarea>
has a maxlength
of 10
, hence we could enter no more than 10 characters in it:
As we try to enter more than 10 characters into it, the surplus data is trimmed off.
Suppose we explicitly mention the current character length along with the maximum length allowed right below the <textarea>
, as follows:
0 / 10
To fancy it, we could say that the <textarea>
is now a 'tracked textarea', as its length is being tracked.
In this exercise, you have to create a TrackedTextarea
component that renders a <textarea>
and writes its current length along with its max length out on the document.
Here's how the length must be displayed: current length / max length. So, for instance, if the current length is 12 and the maximum allowed is 40, we should see 12 / 40.
You should use a normal <p>
element nested with a <small>
element to hold this length information.
Most importantly, the underlying <textarea>
must be uncontrolled.
Here are a couple of points to follow regarding the props of TrackedTextarea
:
- A
maxLength
prop specifies the maximum length of the<textarea>
. By default, it's100
. This prop is also meant to delegated down to the rendered<textarea>
. - A
value
prop, if given, specifies the initial value of the<textarea>
. - However, this
value
prop should NOT be provided to the underlying<textarea>
in order to meet the first requirement above. You have to think of something else to apply an initial value without making the<textarea>
controlled. - If the given
value
prop has more characters than the maximum allowed length of theTrackedTextarea
component, as with the normal behavior in HTML, the value must be presented as it is without truncation. - Any other prop provided must be delegated down to
<textarea>
. For example, if we provide aplaceholder
toTrackedTextarea
, it should be provided to<textarea>
as well.
Here's a dummy code to test the component:
function App() {
return (
<TrackedTextarea value="Initial" maxLength={5} />
);
}
Here's how the final result should look:
Notice how the initial value, i.e. 'Initial'
(7 characters long) is larger in length than the given maxLength
, yet it is displayed without truncation (as asked in the description above).
Hints
Hint 1
The value
prop of TrackedTextarea
should be delegated down to the defaultValue
prop of the <textarea>
.
Hint 2
Create a length
state to keep track of the length of the text entered into the <textarea>
.
New file
Inside the directory you created for this course on React, create a new folder called Exercise-15-Tracked-Textarea and put the .js solution files for this exercise within it.
Solution
Since the exercise has clearly asked us not to make the <textarea>
controlled, it's evident that we don't need to keep track of its value or assign it a value
prop — the DOM should take care of this on its own.
What we need is an onChange
handler assigned to the <textarea>
that reads the length of the data currently entered into it and outputs it on the document along with the provided maxLength
prop.
To keep track of this length, we'll create a length
state, initialized to value.length
, where value
represents the initial value to render in the <textarea>
.
Coming to this value
prop (provided to the TrackedTextarea
component), we need to provide it to the defaultValue
prop of the <textarea>
. Remember that we can't use the value
prop on <textarea>
because doing so would otherwise make the component controlled and that is against the instructions given above.
With all this plan in hand, let's get to implementing TrackedTextarea
.
Here's the complete definition of TrackedTextarea
:
import { useState } from 'react';
function TrackedTextarea({
maxLength = 100,
value,
...props
}) {
const [length, setLength] = useState(value.length);
return (
<>
<textarea
maxLength={maxLength}
defaultValue={value}
onChange={e => setLength(e.target.value.length)}
{...props}
/>
<p><small>{length} / {maxLength}</small></p>
</>
);
}
Let's try running this:
Voila! It works flawlessly!
And this completes this exercise.