Interfaces
A better way to define our ExtendedButton element would be to extend a native HTML button element type like so:
import React, {ButtonHTMLAttributes} from 'react';
interface IButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
/** The text inside the button */
text: string,
/** The type of button, pulled from the Enum ButtonTypes */
type: ButtonTypes,
/** The function to execute once the button is clicked */
action: () => void
}
const ExtendedButton : React.FC<IButtonProps> = ({text, type, action}) => {
}
Enums
//...
/** A set of groupped constants */
enum SelectableButtonTypes {
Important = "important",
Optional = "optional",
Irrelevant = "irrelevant"
}
interface IButtonProps {
text: string,
/** The type of button, pulled from the Enum SelectableButtonTypes */
type: SelectableButtonTypes,
action: (selected: boolean) => void
}
const ExtendedSelectableButton = ({text, type, action}: IButtonProps) => {
let [selected, setSelected] = useState(false)
return (<button className={"extendedSelectableButton " + type + (selected? " selected" : "")} onClick={ _ => {
setSelected(!selected)
action(selected)
}}>{text}</button>)
}
/** Exporting the component AND the Enum */
export { ExtendedSelectableButton, SelectableButtonTypes}
Importing and using Enums:
import React from 'react';
import './App.css';
import {ExtendedSelectableButton, SelectableButtonTypes} from './components/ExtendedSelectableButton/ExtendedSelectableButton'
const App = () => {
return (
<div className="App">
<header className="App-header">
<ExtendedSelectableButton type={SelectableButtonTypes.Important} text="Select me!!" action={ (selected) => {
console.log(selected)
}} />
</header>
</div>
);
}
export default App;
Optional types for your props
//...
interface IProps {
prop1: string,
prop2: number,
myFunction: () => void,
prop3?: boolean //optional prop
}
//...
function MyComponent({...props}: IProps) {
//...
}
/** You can then use them like this */
<mycomponent prop1="text here" prop2=404 myFunction={() = {
//...
}} />
Hooks
Thanks to TypeScript’s type validation, you can enforce the type (or interface) of the initial value of the state, like this:
const [user, setUser] = React.useState<IUser>(user);
Nullable values to hooks
const [user, setUser] = React.useState<IUser | null>(null);
// later...
setUser(newUser);
Generic Components 通用组件
interface Props<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>(props: Props<T>) {
const { items, renderItem } = props;
const [state, setState] = React.useState<T[]>([]);
return (
<div>
{items.map(renderItem)}
</div>
);
}
//type inference
ReactDOM.render(
<List
items={["a", "b"]} // type of 'string' inferred here
renderItem={item => (
<li key={item}>
{item.trim()} //allowed, because we're working with 'strings' all around
</li>
)}
/>,
document.body
);
//directly specifying the data types
ReactDOM.render(
<List<number>
items={[1,2,3,4]}
renderItem={item => <li key={item}>{item.toPrecision(3)}</li>}
/>,
document.body
);
Extending HTML Elements
export interface IBorderedBoxProps extends React.HTMLAttributes<HTMLDivElement> {
title: string;
}
class BorderedBox extends React.Component<IBorderedBoxProps, void> {
public render() {
const {children, title, ...divAttributes} = this.props;
return (
//it is a DIV afterall, and we're trying to let the user use this component knowing that.
<div {...divAttributes} style={{border: "1px solid red"}}>
<h1>{title}</h1>
{children}
</div>
);
}
}
const myBorderedBox = <BorderedBox title="Hello" onClick={() => alert("Hello")}/>;
Event Types
function eventHandler(event: React.MouseEvent<HTMLAnchorElement>) {
console.log("TEST!")
}
const ExtendedSelectableButton = ({text, type, action}: IButtonProps) => {
let [selected, setSelected] = useState(false)
return (<button className={"extendedSelectableButton " + type + (selected? " selected" : "")} onClick={eventHandler}>{text}</button>)
//And you’ll see an error message
//so You can, however, use unions to allow a single handler to be re-used by multiple components:
/** This will allow you to use this event handler both, on anchors and button elements */
function eventHandler(event: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>) {
console.log("TEST!")
}