React provides proptypes for type checking as the following code block demonstrates:
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.propTypes = {
name: PropTypes.string
};
But I can do the following as well for the 'name' proptype:
Greeting.propTypes = {
name: String
};
In the later case i don't need to include the 'prop-types' module. Which one is the recommended approach?
The first way is the recommended way.
When you do
Greeting.propTypes = {
name: String
};
you are defining a javascript string field inside your proptypes object. Also, you will not be able to make the prop a required prop by using the above.
By using this
Greeting.propTypes = {
name: Proptypes.string.isRequired
};
you can make the proptype required and show a console warning if it is not supplied.
Related
In the Creating a component section of React Typescript Starter example, Creating a component, there is a basic React component in Typescript:
// src/components/Hello.tsx
import * as React from 'react';
export interface Props {
name: string;
enthusiasmLevel?: number;
}
function Hello({ name, enthusiasmLevel = 1 }: Props) {
if (enthusiasmLevel <= 0) {
throw new Error('You could be a little more enthusiastic. :D');
}
return (
<div className="hello">
<div className="greeting">
Hello {name + getExclamationMarks(enthusiasmLevel)}
</div>
</div>
);
}
export default Hello;
// helpers
function getExclamationMarks(numChars: number) {
return Array(numChars + 1).join('!');
}
I am new to Typescript. It seems that the interface Props is used by Typescript to do props type checks (similar to what the Proptypes npm package does). So the question is:
If I am already using this kind of Typescript
interface syntax do to props type check, do I still need to use
Proptypes package like this in the same component?
import PropTypes from 'prop-types';
Hello.propTypes = {
name: PropTypes.string,
enthusiasmLevel: PropTypes.number
};
Besides, why does here use export interface? what is the purpose
of exporting the interface Props? Is it compulsory?
Firstly I recommend declaring your components the ES6 way
const Hello: React.FC<IHello> = ({ name, enthusiasmLevel = 1 }) => {}
Your interface defines the contract of your component / The accepted parameters
export interface IHello {
name: string;
enthusiasmLevel?: number;
}
You are exporting this, so you can import your interface from other files / components which want to make use of the Hello component. For example you can use your Hello component like so from another component:
const props: IHello = {
name: "John",
enthusiamsLevel: 5
}
<Hello {...props} />
If I am already using this kind of Typescript interface syntax do to props type check, do I still need to use Proptypes in the same component?
You always want type strong definitions in TypeScript. So when declaring your prop variable in another component, you don't want to do const props: any = {
If you decide to change your interface declaration for this component later on, you would be forced to update all your references which uses this interface. - You might want to require 1 more prop variable and in that case you would want to update your usages of this interface. If you are not used to TypeScript this can seem quite hideous at first - but the benefit of always having strong type definitions will show over time. Especially when you update your type definitions.
I am not using React.
I am using Stenciljs.
I have the following .tsx file:
export class MyComponent {
#Prop() message: string;
render() {
return (<div>{this.message}</div>);
}
}
I want to do this instead:
import myTemplate from '../my-template.??';
export class MyComponent {
#Prop() message: string;
render() {
return (myTemplate);
}
}
with ../my-template.?? containing:
<div>{this.message}</div>
Is it possible and how ? Thanks in advance for any help :)
Yes, you can absolutely do this, there are just a couple of things you need to tidy up:
Main file
import { Template } from '../template'; // No need for file extension but we're using a named export so we need the curly braces around 'Template'
export class MyComponent {
#Prop() message: string;
render() {
return ( // You don't technically need the parentheses here as you're just returning one thing
<Template /> // When outputting an imported component, it goes in angle brackets and the backslash closes it like an HTML element
)
}
}
Template
import React from 'react'; // template needs React
export const Template = () => { // defining the export in this way is known as a named export
return (
<p>A message here</p>
)
}
Okay, so that's going to get you a message output which is from your template. However, you were asking about passing a message to that template for it to output. That's totally easy as well - you just need to get some props in there. Here is the modified version of the above:
Main file
import { Template } from '../template';
export class MyComponent {
#Prop() message: string;
render() {
return (
<Template messageToOutput={message} /> // The first argument is the name of the prop, the second is the variable you defined above
)
}
}
Template
import React from 'react';
export const Template = (props) => { // props are received here
return (
<p>{props.messageToOutput}</p> // props are used here
)
}
That's how you pass data around in React - hope that helps!
Using propTypes to validate props gives the following error:
TypeError: Cannot read property 'string' of undefined.
TypeError: Cannot read property 'func' of undefined.
The code in question is at the bottom of the snippet:
import React from 'react';
import ProjectItem from './ProjectItem';
class Projects extends React.Component {
deleteProject(title) {
this.props.onDelete(title);
}
render() {
let projectItems;
if (this.props.project) {
projectItems = this.props.project.map(project => {
return (
<ProjectItem key={project.title} project={project} onDelete={this.deleteProject.bind(this)} />
)
});
}
return (
<div className="Projects">
{projectItems}
</div>
);
}
}
Projects.propTypes = {
projects: React.PropTypes.string,
onDelete: React.PropTypes.func
}
You need to install the prop-types package and then add the import statement
import PropTypes from prop-types;
at the top of your class.
The PropTypes have been moved from React to their own package prop-types.
EDIT: As mentioned in the comments, this is only applicable for React version 15.5 and above.
As palsrealm mentioned, you need to add the prop-types package, and then remove React before Proptypes. The following should work:
Projects.propTypes = {
projects: PropTypes.string,
onDelete: PropTypes.func
}
I'm sure there's something stupid that I'm missing, but I can't get my TypeScript compiler to compile a fairly simply React component. I'm using a .tsx file extension for my file which TypeScript should be able to handle via my webpack config. I get the following error: ERROR in ./src/App.tsx
(36,16): error TS2339: Property 'todos' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<ToDosList> & Readonly<{ children?: ReactNode; }> &...'.
Below is my code. I've tried both implicitly setting the state for the component (meaning without setting state: App.State) and explicitly giving state a type of App.State, but neither seem to work.
import * as React from 'react';
import ToDosList from './components/todos_list';
const todos = [
{
task: 'Make React tutorial',
is_completed: false
},
{
task: 'Eat Dinner',
is_completed: true
}
];
namespace App {
export interface Props {
}
export interface State {
todos: any
}
}
export default class App extends React.Component<App.Props, App.State> {
constructor(props) {
super(props);
this.state = { todos: todos };
}
render() {
return (
<div>
<h1>React ToDos App</h1>
<ToDosList todos={this.state.todos} />
</div>
);
}
}
I have two modules that I want to share a const array. One of these modules includes both the const array and a component, whilst the other module only includes a component.
This is what I have in module "A".
export const ORDER_COLUMNS = [
{ name: 'orderNumber', title: 'Order', width: '10%', type: 'string' },
{ name: 'orderType', title: 'Type', width: '10%', type: 'string' }
];
class OrderGridControl extends React.Component {
constructor(props) {
super(props);
this.state = {
orderColumns: ORDER_COLUMNS
};
}
...
}
export default OrderGridControl;
And in module "B".
import {OrderGridControl, ORDER_COLUMNS} from 'component/order-grid-control';
class OrderQueryPage extends React.Component {
constructor(props) {
super(props);
this.state = {
orderColumns: ORDER_COLUMNS
};
console.info(this.state.orderColumns);
}
...
render() {
return (
<div>
<PropertyGrid gridSetup={this.state.orderColumns} />
</div>
);
}
}
When I run this I get the following error. invariant.js:39 Uncaught Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of 'moduleB'.
However, the console.info(this.state.orderColumns) line logs all the column objects I expect.
Interestingly, if I copy the array into module "B" and assign the columns in the constructor exactly the same way it seems to work. It only seems to be an issue when I'm importing from the other module.
You've got it almost right-- you're exporting a default export (OrderGridControl) and a named export (ORDER_COLUMNS).
However, in B.js, you're trying to import two named exports.
Modify your import to look like this:
import OrderGridControl, { ORDER_COLUMNS } from 'component/order-grid-control';
The advantage of having a default export is that you don't have to match its name exactly when importing it, so you could do something like
import GridControl, { ORDER_COLUMNS } from 'component/order-grid-control';