In my project, there is an input field to add email tags. which I created using react js. That file working properly, when I convert the file into type script I can't type or paste values to the input field, but there is no error showing in the terminal
js file
import React from "react";
import ReactDOM from "react-dom";
import Chip from "@material-ui/core/Chip";
import "./styles.css";
import TextField from "@material-ui/core/TextField";
class App extends React.Component {
state = {
items: [],
value: "",
error: null,
};
handleKeyDown = evt => {
if (["Enter", "Tab", ","].includes(evt.key)) {
evt.preventDefault();
var value = this.state.value.trim();
if (value && this.isValid(value)) {
this.setState({
items: [...this.state.items, this.state.value],
value: ""
});
}
}
};
handleChange = evt => {
this.setState({
value: evt.target.value,
error: null
});
};
handleDelete = (item) => {
this.setState({
items: this.state.items.filter(i => i !== item),
});
};
handleItemEdit = item =>{
const result = this.state.items.filter(values=>values!==item)
this.setState({
value: item,
error: null,
items: result
});
}
handlePaste = evt => {
evt.preventDefault();
var paste = evt.clipboardData.getData("text");
var emails = paste.match(/[\w\d\.-] @[\w\d\.-] \.[\w\d\.-] /g);
if (emails) {
var toBeAdded = emails.filter(email => !this.isInList(email));
this.setState({
items: [...this.state.items, ...toBeAdded]
});
}
};
isValid(email) {
let error = null;
if (this.isInList(email)) {
error = `${email} has already been added.`;
}
if (!this.isEmail(email)) {
error = `${email} is not a valid email address.`;
}
if (error) {
this.setState({ error });
return false;
}
return true;
}
isInList(email) {
return this.state.items.includes(email);
}
isEmail(email) {
return /[\w\d\.-] @[\w\d\.-] \.[\w\d\.-] /.test(email);
}
render() {
return (
<>
{/* {this.state.items.map(item => (
<div className="tag-item" key={item} onClick={() => this.handleItemEdit(item)}>
{item}
<button
type="button"
className="button"
onClick={(e) => this.handleDelete(e,item)}
>
×
</button>
</div>
))} */}
<TextField id="outlined-basic" variant="outlined"
InputProps={{
startAdornment: this.state.items.map(item => (
<Chip
key={item}
tabIndex={-1}
label={item}
onDelete={() => this.handleDelete(item)}
onClick={() => this.handleItemEdit(item)}
/>
)),
}}
ref={this.state.items}
className={"input " (this.state.error && " has-error")}
value={this.state.value}
placeholder="Type or paste email addresses and press `Enter`..."
onKeyDown={this.handleKeyDown}
onChange={this.handleChange}
onPaste={this.handlePaste}
/>
{this.state.error && <p className="error">{this.state.error}</p>}
</>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
ts file
import React, { useRef, useState } from "react";
import ReactDOM from "react-dom";
import Chip from "@material-ui/core/Chip";
import TextField from "@material-ui/core/TextField";
interface details{
item: string
}
// type Props = {
// test: string,
// // error: string,
// };
export const TagActions = () => {
// var { test } = props
const [items, setItem] = useState<string[]>([]);
const [value, setValue] = useState('')
const [error, setError]= useState('')
const divRef = useRef<HTMLDivElement>(null)
const handleDelete = (item:any) => {
const result = items.filter(i => i !== item)
setItem(result)
};
const handleItemEdit = (item:any) =>{
const result = items.filter(i => i !== item)
setItem(result)
setValue(value)
};
const handleKeyDown = (evt:any) => {
if (["Enter", "Tab", ","].includes(evt.key)) {
evt.preventDefault();
var test = value.trim();
if (test && isValid(test)) {
items.push(test)
setValue("")
}
}
};
const isValid = (email:any)=> {
let error = null;
if (isInList(email)) {
error = `${email} has already been added.`;
}
if (!isEmail(email)) {
error = `${email} is not a valid email address.`;
}
if (error) {
setError(error);
return false;
}
return true;
}
const isInList = (email:any)=> {
return items.includes(email);
}
const isEmail = (email:any)=> {
return /[\w\d\.-] @[\w\d\.-] \.[\w\d\.-] /.test(email);
}
const handleChange = (evt:any) => {
setValue(evt.target.value)
// setError("")
};
const handlePaste = (evt:any) => {
evt.preventDefault();
var paste = evt.clipboardData.getData("text");
var emails = paste.match(/[\w\d\.-] @[\w\d\.-] \.[\w\d\.-] /g);
if (emails) {
var toBeAdded = emails.filter((email:any) => !isInList(email));
setItem(toBeAdded)
}
};
return (
<>
<TextField id="outlined-basic" variant="outlined"
InputProps={{
startAdornment: items.map(item => (
<Chip
key={item}
tabIndex={-1}
label={item}
onDelete={() => handleDelete(item)}
onClick={() => handleItemEdit(item)}
/>
)),
}}
ref={divRef}
value={value}
placeholder="Type or paste email addresses and press `Enter`..."
onKeyDown={(e) => handleKeyDown}
onChange={(e) => handleChange}
onPaste={(e) => handlePaste}
/>
{error && <p className="error">{error}</p>}
</>
);
}
I am a beginner in react typescript. So I don't know what's going on. At first, I referred some documents and applied some solutions, but the expected result did not come. Please share some suggestions to solve this problem
CodePudding user response:
That's a very good start - but there are a few simple issues.
The first big difference between JS and TS is of course the typing - so your functions should definetely be checking that the correct values are being passed! For example:
const isEmail = (email:any)=> {
return /[\w\d\.-] @[\w\d\.-] \.[\w\d\.-] /.test(email);
}
should be:
const isEmail = (email:string)=> {
return /[\w\d\.-] @[\w\d\.-] \.[\w\d\.-] /.test(email);
}
This ensures that you can only pass a string to isEmail
function.
The same goes for the event handlers - one trick I like to use when figuring out the function signature is hovering over the declaration in TSX:
If you hover over onChange
in TSX, it will tell you that its of type React.ChangeEventHandler<HTMLTextAreaElement | HTMLInputElement>
, so to get the type of event evt
in your function, just remove the handler
bit:
const handleChange = (evt: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
// code here
}
Another thing ythat's wrong currently is the way you call the function. In your code you do:
<TextField
id="outlined-basic"
variant="outlined"
...
onChange={e => handleChange}
/>
You aren't actually invoking the handleChange
function, so you could do this:
<TextField
id="outlined-basic"
variant="outlined"
...
onChange={e => handleChange(e)}
/>
Or this instead:
<TextField
id="outlined-basic"
variant="outlined"
...
onChange={handleChange}
/>