1、脚手架启动
npx create-react-app 项目名称 --template typescript
官方建议:
If you've **previously installed create-react-app globally**
via npm install -g create-react-app, we recommend you uninstall
the package using **npm uninstall -g create-react-app** to ensure that npx always uses the latest version.
**Global installs of create-react-app are no longer supported.**
不使用脚手架手动创建:
react和react-dom都需要按照@types对应的声明文件,解析tsx语法使用ts-loader/awesome-typescript-loader/babel
2、创建.tsx文件
3、类型约束(类组件不写接口放到继承组件的泛型里会报错)
(1)类组件
接收参数的组件需要接口声明并使用泛型
方式一:<props泛型,state泛型> 对应有状态组件
方式二:<P,S,SS> 泛型P:参数props,泛型S:状态state,泛型SS:updater和虚拟dom更新相关
方式三:<P> P代表参数props,对应无状态组件
type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;
方式四:<P,S,SS>
PureComponent<P = {}, S = {}, SS = any> extends Component<P, S, SS> { }
class App extends Component<P,S,SS>{ 泛型P:参数props,泛型S:状态state,泛型SS:updater和虚拟dom更新相关
状态:
state:S={
...
}
readonly state:Readonly<S>= { 该方法可避免去改变状态,使用了Readonly映射类型,只对一级属性有用
...
}
构造函数中设置状态:
constructor(props:P) {
super(props);
= {
...
}
}
}
(2)函数组件
function(props:any或者定义接口约束)
{...}
或
import React from 'react'
import React,{FC,SFC} from 'react'
interface P {
count: number
}
const xx:<P> = props => {
其中:
(1)表示无状态函数,泛型P约束props
(2)使用React提供的FC进行约束,会隐式在约束中提供一个children属性,可以之间实现插槽
(3)使用为指定参数赋默认值是,FC约束的函数的接口中的指定属性必须是可选的,普通函数不用
import React from 'react'
interface helo{
name: string,
age?:number
}
const Hello: <helo> = (props) => {
return (<h1>哈哈哈+{}</h1>)
}
= {
age:12
}
export default Hello;
}
const xx:FC<x> = props => {
...
}
(3)高阶函数(通过一个组件返回另一个组件)
1、使用<P>约束传入的组件
泛型P约束props
:<P, any> | <P>的联合类型
2、进行约束
因为传入的组件本身就是一个具有类型约束的组件,所以可以直接使用其泛型(在调用时自动推断参数类型)来进行高阶函数的约束
interface L{
loading:boolean
}
泛型P使得传入高阶函数的参数约束和传入组件的参数一致
function Hello<P>(WrapComp:<P>) { 组件类型:泛型P约束props,是<P, any> | <P>的联合类型
return class extends Component<P & L>{ 通过交叉接口添加高阶函数本身的参数约束
render() {
const { loading, ...props } = ;
return (
loading ? <div>loading</div> : <WrapComp {...props as P}/> 因为不知道剩余参数的类型,使用断言排除报错
)
}
}
}
export default Hello(Wrap);
使用该高阶组件的时候,传入的参数必须满足组件的泛型P代表的约束和交叉接口扩展的约束
(4)Hooks
const [test, setTest] = useState<string | null>(null);
useCallback<()=>void>(()=>{},[])
(4.5)event事件类型约束:
clickHandle = (e:React.事件类型) => {
表单事件,无法获取到value属性
泛型精确化:
<HTMLInputElement> html的input事件,可以获取到value属性
鼠标事件
键盘事件
手势事件
}
(4.6)Promise类型
type c = Promise<string>; 接收一个参数,用来限制resolve传入参数的类型
let c:c=new Promise((resolve,reject)=>{resolve('s')})
(5)路由:
cnpm install react-router-dom --save
cnpm install @types/react-router-dom
其他写法一样
(6)Redxu:
1、安装
cnpm install -S redux react-redux @types/react-redux
不需要安装@types/redux,因为Redux已经自带了声明文件(.文件)。
2、创建types文件夹,其中的文件放置reducer中state的接口格式
export interface IStoreState{
x:类型
}
3、创建文件,放置action的type常量
(1)每个常量包括常量本身及其类型格式,方便在定义action接口时,定义type的类型
(2)当类型格式type名称和常量名称相同时,在导出时,会自动识别接口中的type和常量
export const INCREMENT='INCREMENT'
export type INCREMENT=typeof INCREMENT
4、创建文件
(1)每个action包括及接口格式和自身函数表达式
(2)还要导出使用联合类型声明的接口格式总和
1、方便在组件mapDispatchToPros中,约束dispatch参数格式,
2、方便约束reducer中action的参数格式
(3)引入定义的常量
(4)当常量文件中类型格式type名称和常量名称相同时,在导出时,会自动识别接口中的type和常量
import * as constants from '../constants/index'
export interface IIncrement{
type:
}
export interface IDecrement{
type:
}
export type EnthusiamAction=IIncrement | IDecrement;
export function increment():EnthusiamAction{
return {
type:
}
}
export function decrement():EnthusiamAction{
return {
type:
}
}
5、定义
(1)引入types文件夹中定义的state接口约束,约束state
(2)引入action文件中的对象联合约束类型,约束action
(3)引入action-type中定义的常量,做switch的case
import {EnthusiamAction} from '../actions'
import { IStoreState } from '../types/index';
import {INCREMENT,DECREMENT} from '../constants/index'
export function count(state:IStoreState={count:0},action:EnthusiamAction)
{
switch()
{
case INCREMENT:
return {
count:+1
}
default:
return state
}
}
6、创建文件,导出store
(1)redux-devtools-extension的使用和之前一样
(2)导出store的方式和之前一样
7、在中使用Provider包裹
使用方式和之前一样
8、在组件中使用
(1)从'react-redux'中引入connect
(2)引入types文件夹中的对state的约束接口,约束mapStateToProps的state参数
(3)引入文件中的接口联合类型约束,约束mapDispatchToProps的dispatch函数
mapDispatchToProps(dispatch:Dispatch<x>) Dispatch为type声明的函数格式,泛型约束参数
源码:type Dispatch<A> = (value: A) => void;
(4)组件分别创建props接口和state接口,约束传入的状态和参数,放在继承类的泛型上进行约束
(5)注意在设置action的函数约束时,返回类型要设置成void,不能设置成返回对象
import React,{Component, Dispatch} from 'react'
import * as actions from '../actions/index'
import {IStoreState} from '../types'
import {connect} from 'react-redux'
interface IProps{
title:string,
myclick:(data:string)=>void,
count:number,
increment:()=>void;
decrement:()=>void;
}
interface IState{
count:number
}
class App extends Component<IProps,IState>{
...
}
function mapStateToProps(state:IStoreState){
return{
count:
}
}
function mapDispatchToProps(dispatch:Dispatch<>){ //Dispatch是个函数类型,泛型约定了参数
return{
increment:()=>dispatch(()),
decrement:()=>dispatch(())
}
}
除了显示定义,还可以使用ReturnType
type StateProps=ReturnType<typeof mapStateToProps>
type DispatchProps=ReturnType<typeof mapDispatchToProps>
当涉及到路由时,可以这样定义props
type Props=RouteComponentProps<any>&StateProps&DispatchProps&父组件传递的props
其中:当需要约束路由中参数时,如:这种提示id,可以写成RouteComponentProps<{id:number}>
由如下类型可知,第一泛型参数的内容,会作为match中的提示
export interface RouteComponentProps<
Params extends { [K in keyof Params]?: string } = {},
C extends StaticContext = StaticContext,
S =
> {
history: <S>;
location: <S>;
match: match<Params>;
staticContext?: C | undefined;
}
例:<Props,组件自身state>
export default connect(mapStateToProps,mapDispatchToProps)(App)
4、文件拷贝
ts文件可通过tsconfig指定输出,但其他类型文件不能
方式一:通过shelljs库,将文件拷贝
("R"," public' , "dist") 通过递归拷贝,将public文件夹拷贝的dist目录下
代码示例:
types下的:
export interface IStoreState{
count:number
}
constants下的:
export const INCREMENT='INCREMENT'
export type INCREMENT=typeof INCREMENT
export const DECREMENT='DECREMENT'
export type DECREMENT=typeof DECREMENT
action下的:
import * as constants from '../constants/index'
export interface IIncrement{
type:constants.INCREMENT
}
export interface IDecrement{
type:constants.DECREMENT
}
export type EnthusiamAction=IIncrement | IDecrement;
export function increment():EnthusiamAction{
return {
type:constants.INCREMENT
}
}
export function decrement():EnthusiamAction{
return {
type:constants.DECREMENT
}
}
reducers下的:
import {EnthusiamAction} from '../actions'
import { IStoreState } from '../types/index';
import {INCREMENT,DECREMENT} from '../constants/index'
export function count(state:IStoreState={count:0},action:EnthusiamAction)
{
switch(action.type)
{
case INCREMENT:
return {
count:state.count+1
}
case DECREMENT:
return{
count:state.count-1
}
default:
return state
}
}
store下的:
import {createStore} from 'redux'
import {count} from '../reducers/index'
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(count,composeWithDevTools());
export default store;
:
import React from 'react';
import ReactDOM from 'react-dom';
import './';
import App from './App';
import * as serviceWorker from './serviceWorker';
import AppRouter from './router/appRouter'
import {Provider} from 'react-redux'
import store from './store/index'
ReactDOM.render(
<Provider store={store}>
<React.Fragment>
<AppRouter />
</React.Fragment>
</Provider>
,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: /CRA-PWA
serviceWorker.unregister();
路由文件:
import React,{Component} from 'react'
import {HashRouter,Switch,Route} from 'react-router-dom'
import App from '../App'
class App2 extends Component{
render()
{
return(
<div>
<HashRouter>
<Switch>
<Route exact path='/' component={App}>
</Route>
</Switch>
</HashRouter>
</div>
)
}
}
export default App2
:
import React from 'react';
import logo from './';
import './';
import Hello from './components/hello'
import List from './pages/list'
function App() {
function myclick(data:string)
{
console.log('父组件')
console.log(data);
}
return (
<div className="App">
ts
<Hello title='标题' myclick={myclick}></Hello>
<List></List>
</div>
);
}
export default App;
子组件Hello:
import React,{Component, Dispatch} from 'react'
import { Button } from 'antd';
import * as actions from '../actions/index'
import {IStoreState} from '../types'
import {connect} from 'react-redux'
interface IProps{
title:string,
myclick:(data:string)=>void,
count:number,
increment:()=>void;
decrement:()=>void;
}
interface IState{
count:number
}
class App extends Component<IProps,IState>{
constructor(props:IProps){
super(props);
this.state={
count:2
}
}
// readonly state:Readonly<IState>= {
// count: 1
// }
componentDidMount()
{
// =3;
// ({
// count:5
// });
}
click()
{
this.setState({
count:7
});
}
send()
{
console.log('111');
this.props.myclick('ahh');
}
increment()
{
this.props.increment()
}
decrement()
{
this.props.decrement()
}
render()
{
return(
<div>
hello
{this.props.title}
{this.state.count}
redux:{this.props.count}
<Button onClick={this.click.bind(this)}>点击</Button>
<Button onClick={this.send.bind(this)}>send</Button>
<Button onClick={this.increment.bind(this)}>增加</Button>
<Button onClick={this.decrement.bind(this)}>减少</Button>
</div>
)
}
}
function mapStateToProps(state:IStoreState){
return{
count:state.count
}
}
function mapDispatchToProps(dispatch:Dispatch<actions.EnthusiamAction>){ //Dispatch是个函数类型,泛型约定了参数
return{
increment:()=>{dispatch(actions.increment());},
decrement:()=>dispatch(actions.decrement())
}
}
export default connect(mapStateToProps,mapDispatchToProps)(App)
// import React,{useState} from 'react'
// interface IProps{
// title:string
// }
// function App(props:IProps){
// return(
// <div>
// {}
// </div>
// )
// }
// export default App
子组件List:
import React,{Component} from 'react'
import ListView from '../components/listView'
interface IState {
dataInfo:{
info:number[]
}
}
class App extends Component<{},IState>{
constructor(props:any){
super(props);
this.state={
dataInfo:{
info:[]
}
}
}
componentDidMount()
{
this.setState({
dataInfo:{
info:[1,2,3]
}
})
}
render()
{
return(
<div>
{
this.state.dataInfo.info.length>0?
<div>
<ul>
{
this.state.dataInfo.info.map((item,index)=>{
return <ListView key={index} content={item}></ListView>
})
}
</ul>
</div>: <div>数据加载中</div>
}
</div>
)
}
}
export default App
List子组件ListView:
import React,{Component} from 'react'
interface IProps{
content:number
}
class App extends Component<IProps,any>{
render()
{
return(
<div>
{this.props.content}
</div>
)
}
}
export default App
高阶组件:
import React, { Component } from 'react';
import Wrap from './wrapComp'
interface L{
loading:boolean
}
function Hello<P>(WrapComp:React.ComponentType<P>) { //组件类型:泛型P约束props,是<P, any> | <P>的联合类型
return class extends Component<P & L>{
render() {
const { loading, ...props } = this.props;
return (
loading ? <div>loading</div> : <WrapComp {...props as P}/>
)
}
}
}
export default Hello(Wrap);
高阶组件传入的组件
import React from 'react';
interface helos{
name?: string,
age?: number,
add:string
}
const Hello: React.FC<helos> = (props) => {
return (<h1>哈哈哈+wrap</h1>)
}
export default Hello;
高阶组件调用:
<HelloHigh add={'ww'} loading={false} ></HelloHigh>