TS + 路由

  • 其实也与使用js编写路由是基本一致的,有一些细微的不同就是需要写一些接口来限制参数类型罢了

基本使用

  1. 安装路由(5版本)
npm i react-router-dom@5
  1. 安装预编译文件(路由的类型判断)
npm i --save-dev @types/react-router-dom

案例展示:

  • 要求使用路由实现页面的跳转并且携带参数(动态路由的实现), 以电影为例,使用axios在页面挂载成功时获取数据,点击对应的电影名字,携带对应的电影ID跳转到详情页面

路由组件(index.tsx)

/* ------------------- 路由组件 --------------------- */
import React, { Component } from 'react'
import {HashRouter,Route,Redirect,Switch} from 'react-router-dom'
import Film from './views/Films'// 引入films组件
import Detail from './views/Detail'//引入detail组件
export default class IndexRouter extends Component {
render() {
return (
<HashRouter>
{/* 精准匹配(解决模糊匹配带来的弊端) */}
<Switch>
<Route path="/film" component={Film}/>
{/* 动态路由传参 */}
<Route path="/detail/:myid" component={Detail}/>

{/* 路由的重定向(模糊匹配) */}
<Redirect from="/" to="/film"></Redirect>
</Switch>
</HashRouter>
)
}
}

根组件(App.tsx)

/* --------------------- 根组件 ------------------------ */
import React, { Component } from 'react'
import IndexRouter from './index'//引入路由组件

export default class App extends Component {
render() {
return (
<div>
<IndexRouter/>
</div>
)
}
}

电影列表组件(film.tsx)

/* --------------------- 电影列表组件(views) ------------------------- */
import React, { Component } from 'react'
import axios from 'axios'// 引入axios
import {RouteComponentProps} from 'react-router-dom'// 引入RouteComponentProps

/*
RouteComponentProps:是路由为我们提供的一个泛型,里面基本限制包含我们所要
用到的所有方法或属性,如history,match,location等...

因为使用ts是有着严格的类型限制的,我们在组件中使用props,编译器是不知道props里面
有什么参数,什么类型的,因此我们必须提前声明给编译器听,要它知道props里面有什么
由此我们可以使用两种方法,一种是我们自己定义接口,第二种就是使用路由给我们提供的接口
就是 -> RouteComponentProps
*/

// 自己定义props接口(不推荐使用)
// interface propsIn{
// history:any,
// match:any,
// location:any,
// }

// 定义item的接口限制里面必须有filmId以及name属性
interface itemIn {
filmId:number,
name:string
}

export default class Film extends Component<RouteComponentProps,any> {
// 设置状态
state = {
list:[]
}
// 生命周期页面挂载启动
componentDidMount() {
// 数据请求
axios({
url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=5420934",
headers:{
'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.0","e":"16395416565231270166529"}',
'X-Host': 'mall.film-ticket.film.list'

}
}).then(res=>{
console.log('请求回来的数据',res.data.data.films)
// 将请求回来的数据上传到状态上面
this.setState({
list:res.data.data.films
})
})
}

render() {
return (
<div>
<ul>
{/* 展示数据 */}
{
// item同样要做数据限制
this.state.list.map( (item:itemIn)=>
<li key={item.filmId} onClick={()=>{
// 动态路由传参(将电影id传过去详情页面)
this.props.history.push(`/detail/${item.filmId}`)
}}>{item.name}</li>
)
}
</ul>
</div>
)
}
}

电影详情组件(detail.tsx)[动态路由]

/* --------------------- 电影详情组件(views) ------------------------- */
import React, { Component } from 'react'
import {RouteComponentProps} from 'react-router-dom'

/*
设置接口限制前一页面传过来的参数中必须包含有myid属性,
如果不限制的话,编译器不确定我们穿过过来的参数中是否存在myid属性

RouteComponentProps 本身是泛型,即同样可以使用其他泛型来限制数据
*/
// 设置我们自己的泛型来限制 RouteComponentProps 中携带的数据
interface paramsIn{
myid:string// 必须包含myid且必须为string
}
export default class Detail extends Component<RouteComponentProps<paramsIn>> {
componentDidMount() {
// console.log( (this.props.match.params as any).myid)// 不推荐使用
console.log(this.props.match.params.myid)
}

render() {
return (
<div>
详情页面(电影id:)-{this.props.match.params.myid}
</div>
)
}
}

结果展示:

image

TS + Redux

  • Typescript可以为 StateActionreducer 规定类型、接口、类 加强约束性

案例展示:

  • 使用全局store保存一个标志位,在页面中点击按钮,修改全局标志位,而该标志位是用于判断是否展示底部区域的

全局状态(store.ts)

import {createStore} from 'redux'

// 设置action接口
interface actionIn {
type:string,
payload?:any//设置可选属性payload
}

// 限制初始状态接口
interface stateIn{
isShow:boolean
}

// 设置修改状态函数(reducer)
const reducer = (prevState:stateIn={
isShow:true
},action:actionIn)=>{
// 常规写法
const {type} = action
const newState = {...prevState}
switch(type){
// 接收修改字段 底部展示状态取反
case "changeBottom":
newState.isShow = !newState.isShow
return newState
default :
return prevState
}
}

// 创建全局store
const store = createStore(reducer)

export default store

页面(App.tsx)

import React, { Component } from 'react'
import store from './store'//引入全局store

export default class App extends Component {
// 创建状态
state = {
isShow: store.getState().isShow
}

// 设置生命周期
componentDidMount() {
// 监听全局store中的数据变化
store.subscribe(()=>{
console.log(store.getState())
// 全局数据变化重新赋值状态
this.setState({
isShow:store.getState().isShow
})
})
}
render() {
console.log('全局状态:',store.getState());

return (
<div>
首页
<button onClick={() => {
// 点击使用dispatch修改底部展示标志位
store.dispatch({
type:"changeBottom"
})
}}>关闭底部区域</button>
{/* 根据全局store中的状态判断是否展示底部区域 */}
{this.state.isShow && <div>底部区域</div>}
</div>
)
}
}

结果展示:

image