After switching from JavaScript to TypeScript, I get the error:
Property does not exist on type 'ReadOnly <{}>'
in this code:
this.state.flashcards
It seems that I need to define flashcards as a type, but when I change flashcards: [] to flashcards: any[] it give me an error that any is being used as a value.
How can I get this could to compile in TypeScript as it does in JavaScript?
import React from 'react';
import Flashcard from './Flashcard';
const flashcards = require('../data/custom/itemType_flashcards.json');
class Main extends React.Component {
constructor(props: any) {
super(props);
this.state = {
flashcards: []
};
}
componentDidMount() {
this.setState({
flashcards
});
}
render() {
return (
<div className="content">
{this.state.flashcards.map(flashcard => <Flashcard {...flashcard} key={flashcard.id} />)}
</div>
);
}
}
export default Main;
When using typescript, React.Component is a generic which you can use to specify the types of your props and state. If you don't, the default is for props and state to be empty objects, which is then causing an error when you try to assign a state that isn't an empty object. So do something like the following:
interface MainState {
flashcards: FlashcardProps[]; // You'll need to define the shape of FlashcardProps too
}
class Main extends React.Component<{}, MainState> {
// The rest is unchanged
}
To start off, I have been working with React now for three months and the application I am building is testable, performant, etc... Nothing wrong. My experience pre-React is from the Angular world and what is considered a best practice there is not normally in react and vice-a-versa... I don't think what I am doing is wrong for the application I am building also don't want to miss anything big.
To make a long story short, inside of my App.tsx (using TypeScript) file I am creating a new instance of a singleton service and exporting it as a named export. For example, my app component looks something like:
import * as React from 'react'
... axios import here
import { HttpService } from './core/http.service';
import { Spinner } from './shared/spinner';
const axiosInstance = Axios.create({config here});
const httpService = new HttpService(axiosInstance);
class App extends React.Component {
props: any;
state: any;
constructor(props: any) {
super(props);
}
render() {
return(<App Root Component Here...>)
}
}
export { httpService };
export default App;
Imagine a component somewhere in the app that needs to use my singleton service. For the purposes of my question, I will call the component Home (home/Home.tsx).
import * as React from 'react'
import { httpService } from '../App';
class Home extends React.Component {
props: HomeProps;
state: HomeState;
constructor(props: HomeProps) {
super(props);
this.state = {
isLoading: false,
myData: []
}
this.loadData = this.loadData.bind(this);
}
componentDidMount() {
this.loadData();
}
// Using httpService here.
loadData() {
this.setState({isLoading: true});
httpService.get('/api/somedataurl').then((response) => {
const { data } = response;
this.setState({myData: data});
}).then(() => {
this.setState({isLoading: false});
});
}
myDataList() {
return (<ul>...{map of this.state.myData}</ul>)
}
render() {
return this.state.isLoading ? (<Spinner>) : this.myDataList();
}
}
export default Home;
I decided to use this implementation because I know that I can always rely on the App component to be available and there are no plans for server-side rendering.
As a simple solution, is there anything seriously flawed with providing my singleton service as a named export and importing into other components as needed?
I'm trying to learn ReactJS following this tutorial:
Tutorial
I'm new to the programming language so i'm clueless as to what to do now.
When I try to add the "Fetchemployee.tsx" file, I get an error at the this.state method.
(TS) Property 'state' does not exist on type 'FetchPeriod'
This is the code:
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { Link, NavLink } from 'react-router-dom';
interface FetchPeriodDataState {
periodList: PeriodData[];
loading: boolean;
}
export class FetchPeriod extends React.Component<RouteComponentProps<{}>, FetchPeriodDataState> {
constructor(props) {
super(props);
this.state = { periodList: [], loading: true };
fetch('api/Period/Index')
.then(response => response.json() as Promise<PeriodData[]>)
.then(data => {
this.setState({ periodList: data, loading: false });
});
// This binding is necessary to make "this" work in the callback
this.handleDelete = this.handleDelete.bind(this);
this.handleEdit = this.handleEdit.bind(this);
}
And then later on I have the PeriodData class:
export class PeriodData {
PeriodId: number = 0;
Description: string = "";
PeriodOwner: string = "";
PeriodName: string = "";}
The this.state and this.setState methods are giving the errors in the title, and I can't seem to find a fix.
One of possible causes of the problem is missing the React type definitions
Therefore you can try to install them:
npm install --save-dev #types/react #types/react-dom
Without the types installed, TS can't properly infer the props needed for JSX elements.
I am trying to type the props of my component and use an URL param at the same time. I get the following error:
Property 'match' does not exist on type
'Readonly<{children?:ReactNode}> & Readonly'
Here is some of my code:
import Api from '../../api/Api';
interface MyProps {
api: Api
}
interface MyState {
someString: string,
loading: boolean
}
export class MyComponent extends React.Component<MyProps, MyState> {
constructor(props: MyProps) {
super(props);
this.state = {
someString: this.props.match.params.someString,//<-- Error is here on this line
loading: true
}
}
componentDidMount() {
this.props.api.getSomeInfo(this.state.someString, this.callback)
}
callback() {
let interval = setInterval(function () {
this.setState({loading: false});
clearInterval(interval)
}, 3000);
}
render() {
return (
<div>
<p>{this.someString}</p>
</div>
);
}
}
As you can see all I am trying to do is:
1- Go to:
http://localhost:8080/someStrings/:someString
2- Grab the value of :someString in my component's constructor and store in state
3- Use the value of someString in my state to be able to pass it as an argument to my API module to do stuff
4- When the callback is executed in the API I remove the loading animation
My question is basically, how do I declare my MyProps to be able to acheive this?
This is an open issue in type definition. https://github.com/DefinitelyTyped/DefinitelyTyped/issues/17355
Workaround
import { RouteProps } from 'react-router';
import React from 'react';
interface MyProps {
api: Api
}
interface MyState {
someString: string,
loading: boolean
}
class MyComponent extends React.Component<Props & RouteProps, State> // Pay attention here.
{
// your code...
}
ref: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/17355#issuecomment-336022780
This is how I solved it
import {RouteComponentProps} from 'react-router';
interface IMyProps {}
interface IReactRouterParams {
roomName: string;
username: string;
}
export class MyComponent extends React.Component<
IMyProps & RouteComponentProps<IReactRouterParams> {
constructor(props: any) {
super(props);
//everything works here
const {roomName, username} = this.props.match.params;
}
}
Try to add RouteComponentProps inteface to your props. Change:
export class MyComponent extends React.Component<MyProps, MyState> {
to
export class MyComponent extends React.Component<MyProps & RouteComponentProps, MyState> {
This is my script to handle the match problem, I believe you can borrow something from here.
class MyComponent extends React.Component<Props, State> {
private params: any;
constructor(props: any) {
super(props);
this.state = {
paramId: null
};
}
componentDidMount = () => {
this.getParams();
};
getParams = () => {
this.params = this.props;
this.setState({
paramId: this.params.match.params.id
});
};
render() {
const { paramId } = this.state;
return (
<React.Fragment></React.Fragment>
);
}
}
export default MyComponent;
I adapted the answer of Ritwick Dey into something I find a bit better.
Here it is:
import React, { Component } from 'react';
import { RouteComponentProps } from 'react-router';
interface Props {
// your custom props go here
api: Api
}
interface State {
someString: string,
isLoading: boolean
}
class MyComponent extends Component<RouteComponentProps<Props>, State>
{
// your code...
}
OR
import React, { Component } from 'react';
import { RouteComponentProps } from 'react-router';
interface Props
extends RouteComponentProps <{
// your custom props go here
api: Api
}> {}
interface State {
someString: string,
isLoading: boolean
}
class MyComponent extends Component<Props, State>
{
// your code...
}
They are the same thing. It depends on your preference.
I had a similar issue and that was due to the App.test.jsx file. There, there was already a test case(below code) that I had totally forgotten about and none of the props was utilized in that test case scenario.
When I introduced the props to the test case, it worked. So in nature test was definitely guiding to the right direction since it was trying to use props that do not exists. But after updating the test case with my default props, it works.
Hopefully this helps.
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App **propName1={propwasnotinthetest} propName2={propwasnotinthetest}**/>, div);
ReactDOM.unmountComponentAtNode(div);
});
I experienced a similar issue and found the least type breaking fix is to locally cast the params as any.
someString: (this.props.match.params as any).someString
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
interface RouteComponetPath {
path?: string
}
interface ArticleContainerProps {
another: number
}
interface ArticleContainerState {
path?: string;
}
class ArticleContainer extends React.Component<ArticleContainerProps | RouteComponentProps<RouteComponetPath>, ArticleContainerState> {
constructor(props: ArticleContainerProps | RouteComponentProps<RouteComponetPath>) {
super(props);
this.state = {
path: (this.props as RouteComponentProps<RouteComponetPath>).match.params.path
};
}
componentDidMount() {
console.log("mount! Path is: ", this.state.path);
}
render() {
return (
<h1>This is a page with path {this.state.path} </h1>
)
}
}
export default ArticleContainer;
Which actually makes sense as you can have one interface to handle the paths and use a Union type and a Type Gueard as per TS documentation https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types
So, no hacks and remains strongly typed (not using any any anywhere).
Now I don't see any reason to pass both the path and another type of params, but you know... just to prove it can be done.
RouteComponentProps should help in typescript
import { RouteComponentProps } from 'react-router';
export class Edit extends React.Component<MyProps & RouteComponentProps, MyState> {
constructor(props: MyProps & RouteComponentProps) {
super(props);
...
}
...
(this.props.match.params as any).someString
or because you are using this.props.match but not adding match to the MyProps interface
Easy Workaround would be:
Home extends React.Component<any, any>
I'm getting a ts error from my react component. The component is running fine, building etc, however typescript is showing an error inside the ide. Not sure how i need to declare this to remove the error. I've tried to create a setState method inside the component itself, but this was giving even more errors.
Error:(15, 19) TS2605:JSX element type 'Home' is not a constructor
function for JSX elements. Property 'setState' is missing in type
'Home'.
"typescript": "^2.3.4",
"react": "^15.5.4",
"react-dom": "^15.5.4",
!----
export class App extends React.Component<Props, State> {
public state: State
public props: Props
constructor(props: Props) {
super(props)
this.state = {
view: <Home />, <<<<
}
-- the rest removed for brevity
export class Home extends React.Component<Props, State> {
public state: State;
public props: Props;
constructor(props: Props) {
super(props)
}
public render() {
return <h1>home</h1>
}
}
Had the same issue, but my problem was that I was importing React wrong.
The correct way to import it when using TypeScript is with import * as React from "react".
Code example:
import * as React from "react"
import ReactDOM from "react-dom"
class App extends React.Component<any, any> {
render() {
return (
<div>Hello, World!</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("app"))
Note: <any, any> allows you to use any props and state variables in your React component, but you should usually define those yourself to take advantage of TypeScript's type annotations.
Here's an example of how to use the state and render the components:
type HomeProps = {
text: string;
}
class Home extends React.Component<HomeProps, void> {
public render() {
return <h1>{ this.props.text }</h1>
}
}
type AppState = {
homeText: string;
}
class App extends React.Component<void, AppState> {
constructor() {
super();
this.state = {
homeText: "home"
};
setTimeout(() => {
this.setState({ homeText: "home after change "});
}, 1000);
}
render() {
return <Home text={ this.state.homeText } />
}
}
As you can see, the props and state objects are always simple, and the rendering method is in charge of creating the actual components.
This way react knows which components are changed and which parts of the DOM tree should be updated.