Home > Software design >  Reactjs: Input field not working properly
Reactjs: Input field not working properly

Time:09-02

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)}
            >
              &times;
            </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: enter image description here

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}
/>

I made a enter image description here

  • Related