react ts环境搭建及ts格式写法

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)类组件
	 	接收参数的组件需要接口声明并使用泛型
	 	方式一:React.ComponentClass<props泛型,state泛型> 对应有状态组件
	 	方式二:React.Component<P,S,SS>	泛型P:参数props,泛型S:状态state,泛型SS:updater和虚拟dom更新相关
	 	方式三:React.ComponentType<P>		P代表参数props,对应无状态组件
	 		type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;
	 	方式四:React.PureComponent<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>= {	该方法可避免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<P> = 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<helo> = (props) => {
					  return (<h1>哈哈哈+{props.name}</h1>)
					}
					Hello.defaultProps = {
					  age:12
					}
					
					export default Hello;
		}
		
		const xx:FC<x> = props => {
			...
		}
 
  (3)高阶函数(通过一个组件返回另一个组件)
  		1、使用React.ComponentType<P>约束传入的组件
  			泛型P约束props
  			React.ComponentType:React.ComponentClass<P, any> | React.FunctionComponent<P>的联合类型
  		
  		2、进行约束
  			因为传入的组件本身就是一个具有类型约束的组件,所以可以直接使用其泛型(在调用时自动推断参数类型)来进行高阶函数的约束
  			
	  		interface L{
			  loading:boolean
			}
			
			泛型P使得传入高阶函数的参数约束和传入组件的参数一致
			function Hello<P>(WrapComp:React.ComponentType<P>) {  组件类型:泛型P约束props,是React.ComponentClass<P, any> | React.FunctionComponent<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);
			使用该高阶组件的时候,传入的参数必须满足组件的泛型P代表的约束和交叉接口扩展的约束
			
 (4)Hooks
  	const [test, setTest] = useState<string | null>(null);
  	useCallback<()=>void>(()=>{},[])
  	
 (4.5)event事件类型约束:
 	  clickHandle = (e:React.事件类型) => {
	  	  	React.FormEvent		 	 表单事件,无法获取到value属性
	  	  		泛型精确化:
	  	  			React.Formvent<HTMLInputElement>	html的input事件,可以获取到value属性
	  	  	React.MouseEvent		 鼠标事件
	  	  	React.KeyboardEvent		 键盘事件
	  	  	React.TouchEvent		 手势事件
	  }
	  
 (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已经自带了声明文件(.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<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:state.count
		    }
		}
		
		function mapDispatchToProps(dispatch:Dispatch<actions.EnthusiamAction>){ //Dispatch是个函数类型,泛型约定了参数
		    return{
		        increment:()=>dispatch(actions.increment()),
		        decrement:()=>dispatch(actions.decrement())
		    }
		}
		
		除了显示定义,还可以使用ReturnType
		type StateProps=ReturnType<typeof mapStateToProps>
		type DispatchProps=ReturnType<typeof mapDispatchToProps>
		
		当涉及到路由时,可以这样定义props
		type Props=RouteComponentProps<any>&StateProps&DispatchProps&父组件传递的props
			其中:当需要约束路由中参数时,如:this.props.match.params.id这种提示id,可以写成RouteComponentProps<{id:number}>
				由如下类型可知,第一泛型参数的内容,会作为match中的提示
			export interface RouteComponentProps<
			    Params extends { [K in keyof Params]?: string } = {},
			    C extends StaticContext = StaticContext,
			    S = H.LocationState
			> {
			    history: H.History<S>;
			    location: H.Location<S>;
			    match: match<Params>;
			    staticContext?: C | undefined;
			}
			
		例:React.Component<Props,组件自身state>
		
		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<IState>= {
	// 	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(

//     <div>
//         {props.title}
//     </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,是React.ComponentClass<P, any> | React.FunctionComponent<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>
  • 7
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值