Reputation: 67
I'm trying to write a word counter with React. The idea is that once you exceed the maximum number of words a warning will appear and prevent the user to insert more characters. My idea was to use the maxlength attribute. Once the words needed and the words written are the same amount, the characters will be counted and the maxlength attribute activated through state.
The maxlength attribute doesn't work properly. How can I fix it?
HTML
<div id="app"></div>
REACT
class MyComponent extends React.Component {
constructor(props){
super(props);
this.state = {
firstValue: '',
secondValue: '',
needWords: '',
wordCount: '',
limWords: null,
}
this.firstHandle = this.firstHandle.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.secondHandle = this.secondHandle.bind(this)
}
firstHandle(e){
this.setState({
firstValue: e.target.value
})
}
handleSubmit(e){
e.preventDefault()
this.setState({
needWords: this.state.firstValue
})
}
secondHandle(event){
this.setState({
secondValue: event.target.value,
wordCount: event.target.value === '' ? 0 : this.state.secondValue.split(' ').length,
limWords: (this.state.needWords - this.state.wordCount) < 0 ? this.state.secondValue.length : null
})
}
render(){
var result = this.state.needWords - this.state.wordCount;
let tooManyChars;
if (result < 0){
const tooManyCharStyle = {
color: 'red'
}
tooManyChars = <p style={tooManyCharStyle}>You exceeded the maximum number of words!!</p>;
}
return(
<div>
<form onSubmit={this.handleSubmit}>
<p>How many words do you have to write?</p>
<input
type="text"
value={this.state.firstValue}
onChange={this.firstHandle}></input>
<button type="submit">Go</button>
</form>
<form>
<p>You still have to write {result} words</p>
<textarea
type="text"
value={this.state.value}
onChange={this.secondHandle}
maxLength={this.state.limWords}>
</textarea>
{ tooManyChars }
</form>
</div>
);
}
}
ReactDOM.render(<MyComponent/>, document.getElementById('app'));
Upvotes: 1
Views: 7158
Reputation: 222999
As explained in this answer, maxlength
is applied only to user input, not to programmatically set values.
In order to cover both, secondValue
should be truncated with substr
either in change handler or in render
. maxlength
is optional because excessive length will be handled on field change any way.
Upvotes: 0
Reputation: 408
maxLength takes in the maximum number of characters allowed in that field e.g.
<input type="text" maxLength={10} />
will allow a maximum of 10 characters (not words) in the field.
You need to change your onChange method to monitor changes to the field and accept new characters (but not accept spaces as that dictates a new word). So you can add a conditional like so:
secondHandle(event){
if(event.target.value.split(' ').length > this.state.needWords) {
event.target.value = event.target.value.trimRight();
} else {
this.setState({
secondValue: event.target.value,
wordCount: event.target.value === '' ? 0 : this.state.secondValue.split(' ').length,
limWords: (this.state.needWords - this.state.wordCount) < 0 ? this.state.secondValue.length : null
});
}
}
Upvotes: 0
Reputation: 5476
You have made a couple of mistakes in your code. First thing you need to understand is setState is asynchronous
and if you need to use the current values use them directly. for text area you need to pass this.state.secondValue
instead of this.state.value
and there are couple of other mistakes also. here is the working code.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
firstValue: "",
secondValue: "",
needWords: "",
wordCount: "",
limWords: null
};
this.firstHandle = this.firstHandle.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.secondHandle = this.secondHandle.bind(this);
}
firstHandle(e) {
this.setState({
firstValue: e.target.value
});
}
handleSubmit(e) {
e.preventDefault();
this.setState({
needWords: this.state.firstValue,
secondValue: ""
});
}
secondHandle(event) {
//calculate the word count first itself and use them in other manipulations
const wordCount =
event.target.value === "" ? 0 : event.target.value.split(" ").length;
this.setState({
secondValue: event.target.value,
wordCount: wordCount,
limWords:
this.state.needWords - wordCount < 0
? this.state.secondValue.length
: null
});
}
render() {
var result = this.state.needWords - this.state.wordCount;
let tooManyChars;
if (result < 0) {
const tooManyCharStyle = {
color: "red"
};
tooManyChars = (
<p style={tooManyCharStyle}>
You exceeded the maximum number of words!!
</p>
);
}
return (
<div>
<form onSubmit={this.handleSubmit}>
<p>How many words do you have to write?</p>
<input
type="text"
value={this.state.firstValue}
onChange={this.firstHandle}
/>
<button type="submit">Go</button>
</form>
<form>
<p>You still have to write {result} words</p>
<textarea
type="text"
value={this.state.secondValue}
onChange={this.secondHandle}
maxLength={this.state.limWords}
/>
{tooManyChars}
</form>
</div>
);
}
}
here is the live working example:https://codesandbox.io/s/pk085z3rk7
Upvotes: 2