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)类组件
接收参数的组件需要接口声明并使用泛型
class App extends Component{ 泛型P:参数props,泛型S:状态state,泛型SS:snapshot
状态:
state:S={
...
}
readonly state:Readonly= { 该方法可避免this.state.x去改变状态,使用了Readonly映射类型,只对一级属性有用
...
}
构造函数中设置状态:
constructor(props:P) {
super(props);
this.state = {
...
}
}
}
(2)函数组件
function(props:any或者定义接口约束)
{...}
或
import React from 'react'
import React,{FC,SFC} from 'react'
interface P {
count: number
}
const xx:React.FC
= props => {
其中:
(1)React.SFC表示无状态函数,泛型P约束props
(2)使用React提供的FC进行约束,会隐式在约束中提供一个children属性,可以之间props.children实现插槽
(3)使用xx.defaultProps为指定参数赋默认值是,FC约束的函数的接口中的指定属性必须是可选的,普通函数不用
import React from 'react'
interface helo{
name: string,
age?:number
}
const Hello: React.FC = (props) => {
return (哈哈哈+{props.name}
)
}
Hello.defaultProps = {
age:12
}
export default Hello;
}
const xx:FC = props => {
...
}
(3)高阶函数(通过一个组件返回另一个组件)
1、使用React.ComponentType约束传入的组件
泛型P约束props
React.ComponentType:React.ComponentClass
| React.FunctionComponent
的联合类型
2、进行约束
因为传入的组件本身就是一个具有类型约束的组件,所以可以直接使用其泛型(在调用时自动推断参数类型)来进行高阶函数的约束
interface L{
loading:boolean
}
泛型P使得传入高阶函数的参数约束和传入组件的参数一致
function Hello
(WrapComp:React.ComponentType
) { 组件类型:泛型P约束props,是React.ComponentClass
| React.FunctionComponent
的联合类型
return class extends Component
{ 通过交叉接口添加高阶函数本身的参数约束
render() {
const { loading, ...props } = this.props;
return (
loading ?
loading : 因为不知道剩余参数的类型,使用断言排除报错
)
}
}
}
export default Hello(Wrap);
使用该高阶组件的时候,传入的参数必须满足组件的泛型P代表的约束和交叉接口扩展的约束
(4)Hooks
const [test, setTest] = useState(null);
useCallback<()=>void>(()=>{},[])
(4.5)event事件类型约束:
clickHandle = (e:React.事件类型) => {
React.FormEvent 表单事件,无法获取到value属性
泛型精确化:
React.FormEvent html的input事件,可以获取到value属性
React.MouseEvent 鼠标事件
React.KeyboardEvent 键盘事件
React.TouchEvent 手势事件
}
(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已经自带了声明文件(.d.ts文件)。
2、创建types文件夹,其中的index.tsx文件放置reducer中state的接口格式
export interface IStoreState{
x:类型
}
3、创建action-type.tsx文件,放置action的type常量
(1)每个常量包括常量本身及其类型格式,方便在定义action接口时,定义type的类型
(2)当类型格式type名称和常量名称相同时,在导出时,会自动识别接口中的type和常量
export const INCREMENT='INCREMENT'
export type INCREMENT=typeof INCREMENT
4、创建action.tsx文件
(1)每个action包括及接口格式和自身函数表达式
(2)还要导出使用联合类型声明的接口格式总和
1、方便在组件mapDispatchToPros中,约束dispatch参数格式,
2、方便约束reducer中action的参数格式
(3)引入定义的常量
(4)当常量文件中类型格式type名称和常量名称相同时,在导出时,会自动识别接口中的type和常量
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
}
}
5、定义reducer.tsx
(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(action.type)
{
case INCREMENT:
return {
count:state.count+1
}
default:
return state
}
}
6、创建store.js文件,导出store
(1)redux-devtools-extension的使用和之前一样
(2)导出store的方式和之前一样
7、在index.tsx中使用Provider包裹
使用方式和之前一样
8、在组件中使用
(1)从'react-redux'中引入connect
(2)引入types文件夹中的对state的约束接口,约束mapStateToProps的state参数
(3)引入action.tsx文件中的接口联合类型约束,约束mapDispatchToProps的dispatch函数
mapDispatchToProps(dispatch:Dispatch) Dispatch为type声明的函数格式,泛型约束参数
源码:type Dispatch = (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{
...
}
function mapStateToProps(state:IStoreState){
return{
count:state.count
}
}
function mapDispatchToProps(dispatch:Dispatch){ //Dispatch是个函数类型,泛型约定了参数
return{
increment:()=>dispatch(actions.increment()),
decrement:()=>dispatch(actions.decrement())
}
}
export default connect(mapStateToProps,mapDispatchToProps)(App)
4、文件拷贝
ts文件可通过tsconfig指定输出,但其他类型文件不能
方式一:通过shelljs库,将文件拷贝
shelljs.cp("R"," public' , "dist") 通过递归拷贝,将public文件夹拷贝的dist目录下
代码示例:
types下的index.tsx:
export interface IStoreState{
count:number
}
constants下的index.tsx:
export const INCREMENT='INCREMENT'
export type INCREMENT=typeof INCREMENT
export const DECREMENT='DECREMENT'
export type DECREMENT=typeof DECREMENT
action下的index.tsx:
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下的index.tsx:
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下的index.tsx:
import {
createStore} from 'redux'
import {
count} from '../reducers/index'
import {
composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(count,composeWithDevTools());
export default store;
index.tsx:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
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: https://bit.ly/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
App.tsx:
import React from 'react';
import logo from './logo.svg';
import './App.css';
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= {
// count: 1
// }
componentDidMount()
{
// this.state.count=3;
// this.setState({
// 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(
//
// {props.title}
//
// )
// }
// 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,是React.ComponentClass | React.FunctionComponent
的联合类型
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>