简体   繁体   中英

TypeScript errors in React Class Component property does not exist on type 'Readonly<{}>', not sure how to set types for state

I have a React Class Component with two properties in state. I'm currently getting a TypeScript error when trying to dynamically setState that says Property 'input' does not exist on type 'Readonly<{}>' .

I'm newer to TypeScript and I haven't yet had to tackle the problem of adding Type definitions to a Class Component before. I've been working mostly with functional components and hooks so this issue hasn't come up for me.

I defined the type for my App State and then passed it into the component, but I'm still getting the original error as well as a new error where I define state that says 'AppState' only refers to a type, but is being used as a value here.

I've been searching around for a solution but haven't been able to solve this.

My original component

type AppState = {
  input: string;
  imageUrl: string;
}

class App extends Component<AppState> {
  constructor(props: any) {
    super(props);
    // Error: 'AppState' only refers to a type, but is being used as a value here.
    this.state: AppState = {
      input: "",
      imageUrl: "",
    };
  }

  onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    this.setState({ input: e.target.value });
  };

  onButtonSubmit = () => {
    // Error: Property 'input' does not exist on type 'Readonly<{}>'.
    this.setState({ imageUrl: this.state.input });
    clarifaiApp.models
      .predict(
        Clarifai.COLOR_MODEL,
        // URL
        "https://samples.clarifai.com/metro-north.jpg"
      )
      .then(
        function (response: any) {
          console.log("This is your response: ", response);
        },
        function (err: Error) {
          console.log("There was an error: ", err);
        }
      );
  };

  render() {
    return (
      <Container>
        <ImageLinkForm
          onInputChange={this.onInputChange}
          onButtonSubmit={this.onButtonSubmit}
        />
        {/* Error: Property 'imageUrl' does not exist on type 'Readonly<{}>'. */}
        <FaceRecognition imageUrl={this.state.imageUrl} />
      </Container>
    );
  }
}

export default App;

If you want to initialize the state inside the constructor of a class component you have to provide two type argument to the React.Component generic. The first argument is meant to provide type information about the props and the second argument is meant to provide the type information about the state . For example,

interface AppProps {
  // props 
}

type AppState = {
  input: string;
  imageUrl: string;
}

class App extends Component<AppProps,AppState> {
  // you don't need any more explicit type annotation here
  constructor(props) {
    super(props);
    this.state = {
      input: "",
      imageUrl: "",
    };
  }

  // rest of the app logic
}

This would not have caused any issue if you would have initialized the state in the state field outside the constructor.

type AppState = {
  input: string;
  imageUrl: string;
}

class App extends Component {
  state: AppState = {
    input: "",
    imageUrl: ""
  }


  // rest of the app logic
}

In the second case, you can choose to provide a type argument for the props if the component is expecting some props. For example,

interface AppProps {
  // type info for props
}

type AppState = {
  input: string;
  imageUrl: string;
}

class App extends Component<AppProps> {
   state: AppState = {
      input: "",
      imageUrl: ""
   }

   // rest of the app logic
}

For React class components, you will generally need to supply the generic prop parameters with the types for the props and state respectively.

class App extends Component<AppProps, AppState> {

  ...

}

For your case, since there are no props for the App component, you will only need to do this:

class App extends Component<{}, AppState> {
  constructor(props: {}) {
    super(props);
    this.state: AppState = {
      input: "",
      imageUrl: "",
    };
  }    
  ...

}

if you want to try it with the hooks then you can do this.

import React, { useState } from "react";

interface AppState {
  input: string;
  imageUrl: string;
}

const App: React.FC<AppState> = () => {
  const [input, setInput] = useState("");
  const [imageUrl, setImageUrl] = useState("");

  const handleOnButtonSubmit = () => {
    setImageUrl(input);
    clarifaiApp.models
      .predict(
        Clarifai.COLOR_MODEL,
        // URL
        "https://samples.clarifai.com/metro-north.jpg"
      )
      .then(
        function(response: any) {
          console.log("This is your response: ", response);
        },
        function(err: Error) {
          console.log("There was an error: ", err);
        }
      );
  };

  return (
    <Container>
      <ImageLinkForm
        onInputChange={(e: any) => setInput(e.target.value)}
        onButtonSubmit={handleOnButtonSubmit}
      />
      <FaceRecognition imageUrl={imageUrl} />
    </Container>
  );
};

export default App;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM