赞
踩
为什么会这样?
代码编译和代码执行的顺序:1 编译 2 执行
npm i -g typescript
// 或者
yarn global add typescript
Mac 电脑安装全局包时,需要添加 sudo
获取权限:sudo npm i -g typescript
* yarn 全局安装:sudo yarn global add typescript
tsc –v
(查看 typescript 的版本)
number/string/boolean/null/undefined
object
(包括,数组、对象、函数等对象)联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any 等
注意:
let age = 18
let age: number = 18 // : number 就是类型注解
let age: number = 18
let myName: string = '老师'
let isLoading: boolean = false
// 等等...
// 写法一:
let numbers: number[] = [1, 3, 5] //数组中只允许number类型
// 写法二:
let strings: Array<string> = ['a', 'b', 'c'] //泛型写法,后面补充
let arr: (number | string)[] = [1, 'a', 3, 'b'] // 数组中允许number和string类型
类型别名(自定义类型)
:为任意类型起别名type CustomArray = (number | string)[]
let arr1: CustomArray = [1, 'a', 3, 'b']
let arr2: CustomArray = ['x', 'y', 6, 7]
type
关键字来创建自定义类型// 函数声明
function add(num1: number, num2: number): number {
return num1 + num2
}
// 箭头函数
const add = (num1: number, num2: number): number => {
return num1 + num2
}
type AddFn = (num1: number, num2: number) => number
const add: AddFn = (num1, num2) => {
return num1 + num2
}
void类型
可选参数
function mySlice(start?: number, end?: number): void {
console.log('起始索引:', start, '结束索引:', end)
}
let func3 = (a: number = 1): number => a
// 空对象
let person: {} = {}
// 有属性的对象
let person: { name: string } = {
name: '同学'
}
// 既有属性又有方法的对象
// 在一行代码中指定对象的多个属性类型时,使用 `;`(分号)来分隔
let person: { name: string; sayHi(): void } = {
name: 'jack',
sayHi() {}
}
// 对象中如果有多个类型,可以换行写:
// 通过换行来分隔多个属性类型,可以去掉 `;`
let person: {
name: string
sayHi(): void
} = {
name: 'jack',
sayHi() {}
}
let obj: {
name: string;
age?: number;
do: (what: string) => string;
eat?(what1: string): string
} = {
name: 'glm',
do: (what) => {
return what;
}
};
// 创建类型别名
type Person = {
name: string
sayHi(): void
}
// 使用类型别名作为对象的类型:
let person: Person = {
name: 'jack',
sayHi() {}
}
interface IPerson {
name: string
age: number
sayHi(): void
}
let person: IPerson = {
name: 'jack',
age: 19,
sayHi() {}
}
interface Point2D { x: number; y: number }
interface Point3D { x: number; y: number; z: number }
interface Point2D { x: number; y: number }
// 继承 Point2D
interface Point3D extends Point2D {
z: number
}
let position: number[] = [116.2317, 39.5427]
元组 Tuple
let position: [number, number] = [39.5427, 116.2317]
// 变量 age 的类型被自动推断为:number
let age = 18
// 函数返回值的类型被自动推断为:number
function add(num1: number, num2: number) {
return num1 + num2
}
let str1 = 'Hello TS'
const str2 = 'Hello TS'
通过 TS 类型推论机制,可以得到答案:
解释:
{ name: 'jack' }
[]
18
20
'abc'
false
function() {}
// 使用自定义类型:
type Direction = 'up' | 'down' | 'left' | 'right'
function changeDirection(direction: Direction) {
console.log(direction)
}
// 调用函数时,会有类型提示:
changeDirection('up')
// Down -> 11、Left -> 12、Right -> 13
enum Direction { Up = 10, Down, Left, Right }
enum Direction { Up = 2, Down = 4, Left = 8, Right = 16 }
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
let obj: any = { x: 0 }
obj.bar = 100
obj()
const n: number = obj
const aLink = document.getElementById('link') as HTMLAnchorElement
function id<T>(value: T): T { return value }
// 调用泛型函数
const num = id<number>(10)
const str = id<string>('a')
interface IdFunc<Type> {
id: (value: Type) => Type
ids: () => Type[]
}
let obj: IdFunc<number> = {
id(value) { return value },
ids() { return [1, 3, 5] }
}
type Props = {
id: string
children: number[]
}
type PartialProps = Partial<Props>
type Props = {
id: string
children: number[]
}
type ReadonlyProps = Readonly<Props>
let props: ReadonlyProps = { id: '1', children: [] }
// 错误演示
props.id = '2'
interface Props {
id: string
title: string
children: number[]
}
type PickProps = Pick<Props, 'id' | 'title'>
命令:npx create-react-app my-app --template typescript
说明:在命令行中,添加 --template typescript
表示创建支持 TS 的项目
项目目录的变化:
tsconfig.json
.ts
或 .tsx
.ts
ts 文件的后缀名.tsx
是在 TS 中使用 React 组件时,需要使用该后缀,只要文件中使用了jsx模板,后缀名必须叫tsxreact-app-env.d.ts
文件
.d.ts
类型声明文件,用来指定类型tsconfig的介绍
tsc --init
生成{
// 编译选项
"compilerOptions": {
// 生成代码的语言版本:将我们写的 TS 代码编译成哪个版本的 JS 代码
// 命令行: tsc --target es5 11-测试TS配置文件.ts
"target": "es5",
// 指定要包含在编译中的 library
"lib": ["dom", "dom.iterable", "esnext"],
// 允许 ts 编译器编译 js 文件
"allowJs": true,
// 跳过类型声明文件的类型检查
"skipLibCheck": true,
// es 模块 互操作,屏蔽 ESModule 和 CommonJS 之间的差异
"esModuleInterop": true,
// 允许通过 import x from 'y' 即使模块没有显式指定 default 导出
"allowSyntheticDefaultImports": true,
// 开启严格模式
"strict": true,
// 对文件名称强制区分大小写 Demo.ts
"forceConsistentCasingInFileNames": true,
// 为 switch 语句启用错误报告
"noFallthroughCasesInSwitch": true,
// 生成代码的模块化标准
"module": "esnext",
// 模块解析(查找)策略
"moduleResolution": "node",
// 允许导入扩展名为.json的模块
"resolveJsonModule": true,
// 是否将没有 import/export 的文件视为旧(全局而非模块化)脚本文件
"isolatedModules": true,
// 编译时不生成任何文件(只进行类型检查)
"noEmit": true,
// 指定将 JSX 编译成什么形式
"jsx": "react-jsx"
},
// 指定允许 ts 处理的目录
"include": ["src"]
}
今天几乎所有的 JavaScript 应用都会引入许多第三方库来完成任务需求。
这些第三方库不管是否是用 TS 编写的,最终都要编译成 JS 代码,才能发布给开发者使用。
我们知道是 TS 提供了类型,才有了代码提示和类型保护等机制。
但在项目开发中使用第三方库时,你会发现它们几乎都有相应的 TS 类型,这些类型是怎么来的呢? 类型声明文件
类型声明文件:用来为已存在的 JS 库提供类型信息
TS 中有两种文件类型:1 .ts
文件 2 .d.ts
文件
.ts 文件:
既包含类型信息又可执行代码
.d.ts 文件:
只包含类型信息
的类型声明文件总结:.ts 是 implementation
(代码实现文件);.d.ts 是 declaration(类型声明文件)
如果要为 JS 库提供类型信息,要使用 .d.ts
文件
const strs = ['a', 'b', 'c']
// 鼠标放在 forEach 上查看类型
strs.forEach
lib.es5.d.ts
类型声明文件中lib.dom.d.ts
)node_modules/axios
目录解释:这种情况下,正常导入该库,TS 就会自动加载库自己的类型声明文件,以提供该库的类型声明。
@types/*
import _ from 'lodash'
// 在 VSCode 中,查看 'lodash' 前面的提示
@types/*
类型声明包后,TS 也会自动加载该类声明包,以提供该库的类型声明项目内共享类型
为已有 JS 文件提供类型声明
类型声明文件的使用说明
let count = 10
let songName = '痴心绝对'
let position = {
x: 0,
y: 0
}
function add(x, y) {
return x + y
}
function changeDirection(direction) {
console.log(direction)
}
const fomartPoint = point => {
console.log('当前坐标:', point)
}
export { count, songName, position, add, changeDirection, fomartPoint }
定义类型声明文件
declare let count:number
declare let songName: string
interface Position {
x: number,
y: number
}
declare let position: Position
declare function add (x :number, y: number) : number
type Direction = 'left' | 'right' | 'top' | 'bottom'
declare function changeDirection (direction: Direction): void
type FomartPoint = (point: Position) => void
declare const fomartPoint: FomartPoint
export {
count, songName, position, add, changeDirection, FomartPoint, fomartPoint
}
**目标:**掌握useState hooks配合typescript使用
内容:
useState
接收一个泛型参数,用于指定初始值的类型useState
的源码如下/**
* Returns a stateful value, and a function to update it.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usestate
*/
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
useState
的使用const [name, setName] = useState<string>('张三')
const [age, setAge] = useState<number>(28)
const [isProgrammer, setIsProgrammer] = useState<boolean>(true)
// 如果你在set函数中的参数不符合声明的变量类型,程序会报错
<button onClick={() => setName(100)}>按钮</button> // 报错
useState
的类型推断,在使用useState的时候,只要提供了初始值,typescript会自动根据初始值进行类型推断,因此useState
的泛型参数可以省略export default function App() {
const [name, setName] = useState('张三')
const [age, setAge] = useState(28)
const [isProgrammer, setIsProgrammer] = useState(true)
return (
<div>
<button onClick={() => setName(100)}>按钮</button>
</div>
)
}
**目标:**掌握useEffect hook在typescript中的使用
内容
useEffect
是用于我们管理副作用(例如 API 调用)并在组件中使用 React 生命周期的useEffect
的源码/**
* Accepts a function that contains imperative, possibly effectful code.
*
* @param effect Imperative function that can return a cleanup function
* @param deps If present, effect will only activate if the values in the list change.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useeffect
*/
function useEffect(effect: EffectCallback, deps?: DependencyList): void;
useEffect
函数不涉及到任何泛型参数,在typescript中使用和javascript中使用完全一致。useEffect(() => {
// 给 window 绑定点击事件
const handleClick = () => {
console.log('哈哈哈')
}
window.addEventListener('click', handleClick)
return () => {
// 给 window 移除点击事件
window.addEventListener('click', handleClick)
}
}, [])
**目标:**能够使用useEffect发送请求并且配合useState进行渲染
内容:
频道列表接口:http://geek.itheima.net/v1_0/channels
需求,发送请求获取频道列表数据,并且渲染
**注意:**useState如果没有提供具体类型的初始值,是需要使用泛型参数指定类型的。
// 存放频道列表数据
// 如果给useState的泛型参数直接指定为一个[],那将会得到一个never类型的数据,渲染的时候会出问题
const [list, setList] = useState([])
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ryj7Gmzz-1652862937632)(images/image-20211121180010600.png)]
import { useEffect, useState } from 'react'
import axios from 'axios'
type Res = {
id: number
name: string
}[]
export default function App() {
// 存放频道列表数据
const [list, setList] = useState<Res>([])
useEffect(() => {
const fetchData = async () => {
const res = await axios.get('http://geek.itheima.net/v1_0/channels')
setList(res.data.data.channels)
}
fetchData()
}, [])
return (
<div>
<ul>
{list.map((item) => {
return <li key={item.id}>{item.name}</li>
})}
</ul>
</div>
)
}
**目标:**能够使用useRef配合ts操作DOM
内容:
useRef
接收一个泛型参数,源码如下/**
* `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
* (`initialValue`). The returned object will persist for the full lifetime of the component.
*
* Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
* value around similar to how you’d use instance fields in classes.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useref
*/
function useRef<T>(initialValue: T): MutableRefObject<T>;
interface MutableRefObject<T> {
current: T;
}
useRef
的泛型参数用于指定current属性的值的类型
如果使用useRef操作DOM,需要明确指定所操作的DOM的具体的类型,否则current属性会是null
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XLStKS8K-1652862937635)(images/image-20211121181806382.png)]
const inputRef = useRef<HTMLInputElement>(null)
const get = () => {
console.log(inputRef.current?.value)
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CLNyw3fa-1652862937636)(images/image-20211121182048771.png)]
**目标:**掌握js中的提供的可选链操作符语法
内容
?.
)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。let nestedProp = obj.first?.second;
console.log(res.data?.data)
obj.fn?.()
if (obj.fn) {
obj.fn()
}
obj.fn && obj.fn()
// 等价于
let temp = obj.first;
let nestedProp = ((temp === null || temp === undefined) ? undefined : temp.second);
**目标:**掌握ts中的非空断言的使用语法
内容:
!
// 告诉typescript, 明确的指定obj不可能为空
let nestedProp = obj!.second;
**目标:**能够在typescript中使用react路由
内容:
yarn add @types/react-router-dom
Home.tsx
和Login.tsx
import { BrowserRouter as Router, Link, Route } from 'react-router-dom'
import Home from './pages/Home'
import Login from './pages/Login'
export default function App() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/home">首页</Link>
</li>
<li>
<Link to="/login">登录页</Link>
</li>
</ul>
<div>
<Route path="/home" component={Home}></Route>
<Route path="/login" component={Login}></Route>
</div>
</div>
</Router>
)
}
**目标:**掌握useHistory在typescript中的使用
内容:
useHistory可以实现路由之间的跳转,并且在跳转时可以指定跳转参数state的类型
useHistory的源码如下
export function useHistory<HistoryLocationState = H.LocationState>(): H.History<HistoryLocationState>;
const history = useHistory()
const login = () => {
history.push('/login')
}
const history = useHistory<{
aa: string
}>()
const login = () => {
history.push({
pathname: '/login',
state: {
aa: 'cc',
},
})
}
**目标:**掌握useLocation在typescript中的使用
内容:
export function useLocation<S = H.LocationState>(): H.Location<S>;
import { useLocation } from 'react-router'
export default function Home() {
const location = useLocation<{ aa: string } | null>()
const aa = location.state?.aa
return <div>Home组件---{aa}</div>
}
**注意:**因为useLocation和useHistory都需要指定Location类型,因此可以将类型存放到通用的类型声明文件中
// types.d.ts
export type LoginState = {
aa: string
} | null
目标:能够掌握useParams在typescript中的使用
内容:
import { useParams } from 'react-router'
export default function Article() {
const params = useParams<{ id: string }>()
console.log(params.id)
return (
<div>
文章详情
<div>12</div>
</div>
)
}
**目标:**了解什么是TS中的unknown类型
内容:
// 没有类型检查就没有意义了,跟写JS一样。很不安全。
let value:any
value = true
value = 1
value.length
let value:unknown
value = 'abc'
(value as string).length
if (typeof value === 'string') {
value.length
}
**目标:**掌握在ts项目中如何初始化redux
内容:
yarn add redux react-redux redux-devtools-extension
import { createStore } from 'redux'
import reducer from './reducers'
import { composeWithDevTools } from 'redux-devtools-extension'
const store = createStore(reducer, composeWithDevTools())
export default store
import { combineReducers } from 'redux'
import todos from './todos'
const rootReducer = combineReducers({
todos,
})
export default rootReducer
const initValue = [
{
id: 1,
name: '吃饭',
done: false,
},
{
id: 2,
name: '睡觉',
done: true,
},
{
id: 3,
name: '打豆豆',
done: false,
},
]
export default function todos(state = initValue, action: any) {
return state
}
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import store from './store'
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
**目标:**掌握useSelector在ts中的使用
内容
export function useSelector<TState = DefaultRootState, TSelected = unknown>(
selector: (state: TState) => TSelected,
equalityFn?: (left: TSelected, right: TSelected) => boolean
): TSelected;
// 获取todos数据
const todos = useSelector<{ name: string }, string>((state) => state.name)
const todos = useSelector((state: { name: string }) => state.name)
问题:如何准确的获取到store的类型??
**目标:**能够掌握如何获取redux的rootState
内容:
参考文档:https://react-redux.js.org/using-react-redux/usage-with-typescript
typeof
可以获取某个数据的类型
function fn(n1: number, n2:number):number {
return n1 + n2
}
// 获取fn函数的类型
type Fn = typeof fn
ReturnType
是一个泛型工具类型,可以获取一个函数类型的返回值类型function fn(n1: number, n2:number):number {
return n1 + n2
}
// 获取fn函数的类型
type Fn = typeof fn
// 获取Fn函数的返回值类型
type Res = ReturnType<Fn>
store/index.tx
export type RootState = ReturnType<typeof store.getState>
import { RootState } from '../store'
// 获取todos数据
const todos = useSelector((state: RootState) => state.todos)
**目标:**掌握reducers在TS中的写法
内容:
export function addTodo(name: string) {
return {
type: 'ADD_TODO',
name,
}
}
export function delTodo(id: number) {
return {
type: 'DEL_TODO',
id,
}
}
export type TodoAction =
| {
type: 'ADD_TODO'
name: string
}
| {
type: 'DEL_TODO'
id: number
}
export const addTodo = (name: string): TodoAction => {
return {
type: 'ADD_TODO',
name
}
}
export const delTodo = (id: number): TodoAction => {
return {
type: 'DEL_TODO',
id
}
}
type TodosList = {
id: number
name: string
done: boolean
}[]
const initValue: TodosList = []
export default function todos(state = initValue, action: any): TodosList {
return state
}
import { TodoAction } from '../actions/todos'
import { TodoAction } from '../types'
export default function todos(
state = initValue,
action: TodoAction
): TodosList {
if (action.type === 'ADD_TODO') {
return [
{
id: Date.now(),
name: action.name,
done: false,
},
...state,
]
}
if (action.type === 'DEL_TODO') {
return state.filter((item) => item.id !== action.id)
}
return state
}
**目标:**掌握useDispatch在ts中的使用
内容:
const dispatch = useDispatch()
<button onClick={() => dispatch(delTodo(item.id))}>x</button>
**目标:**掌握事件对象在TS中如何指定类型
内容:
const add = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.code === 'Enter') {
dispatch(addTodo(name))
setName('')
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CmnCdEVO-1652862937637)(images/image-20211123215525876.png)]
**目标:**掌握redux thunk在typescript中的使用
内容:
yarn add redux-thunk
import thunk from 'redux-thunk'
const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3elGgOL5-1652862937638)(images/image-20211123221615977.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dfSAA49F-1652862937638)(images/image-20211123221710008.png)]
export type RootThunkAction = ThunkAction<void, RootState, unknown, TodoAction>
// 修改删除Action
export function delTodo(id: number): RootThunkAction {
return (dispatch) => {
setTimeout(() => {
dispatch({
type: 'DEL_TODO',
id,
})
}, 1000)
}
}
在redux-thunk@2.4.0新版中,使用dispatch的时候,会丢失提示,需要降级到2.3.0版本
https://github.com/reduxjs/redux-thunk/issues/326
yarn add redux-thunk@2.3.0
引入通用样式(资料中已经准备好)
import './styles/index.css'
封装频道组件和新闻列表组件
components/Channel.js
import React from 'react'
export default function Channel() {
return (
<ul className="catagtory">
<li className="select">开发者资讯</li>
<li>ios</li>
<li>c++</li>
<li>android</li>
<li>css</li>
<li>数据库</li>
<li>区块链</li>
<li>go</li>
<li>产品</li>
<li>后端</li>
<li>linux</li>
<li>人工智能</li>
<li>php</li>
<li>javascript</li>
<li>架构</li>
<li>前端</li>
<li>python</li>
<li>java</li>
<li>算法</li>
<li>面试</li>
<li>科技动态</li>
<li>js</li>
<li>设计</li>
<li>数码产品</li>
<li>html</li>
<li>软件测试</li>
<li>测试开发</li>
</ul>
)
}
components/NewsList.js
import React from 'react'
import avatar from '../assets/back.jpg'
export default function NewsList() {
return (
<div className="list">
<div className="article_item">
<h3 className="van-ellipsis">python数据预处理 :数据标准化</h3>
<div className="img_box">
<img src={avatar} className="w100" alt="" />
</div>
<div className="info_box">
<span>13552285417</span>
<span>0评论</span>
<span>2018-11-29T17:02:09</span>
</div>
</div>
</div>
)
}
根组件中渲染
import React from 'react'
import Channel from './components/Channel'
import NewsList from './components/NewsList'
export default function App() {
return (
<div className="app">
<Channel></Channel>
<NewsList></NewsList>
</div>
)
}
准备静态资源
获取频道列表
http://geek.itheima.net/v1_0/channels
获取频道新闻
http://geek.itheima.net/v1_0/articles?channel_id=频道id×tamp=时间戳
步骤:
在store/reducers/channel.ts
const initValue = {
channelList: [],
active: 0
}
export default function channel(state = initValue, action: any) {
return state
}
在store/actions/channel.ts
import axios from 'axios'
import { RootThunkAction } from '..'
export function getChannelList(): RootThunkAction {
return async (dispatch) => {
const res = await axios.get('http://geek.itheima.net/v1_0/channels')
console.log(res)
}
}
在components/Channel.tsx中
export default function Channel() {
const dispatch = useDispatch()
useEffect(() => {
dispatch(getChannelList())
}, [dispatch])
在store/actions/channel.ts
import axios from 'axios'
import { RootThunkAction } from '..'
// 1. 提供了Channel的类型
export type Channel = {
id: number
name: string
}
// 2. 提供了ChannelAction的类型
export type ChannelAction = {
type: 'channel/getChannelList'
payload: Channel[]
}
export function getChannelList(): RootThunkAction {
return async (dispatch) => {
const res = await axios.get('http://geek.itheima.net/v1_0/channels')
// 3. dispatch ChannelAction 问题: dispatch的时候没有提示
dispatch({
type: 'channel/getChannelList',
payload: res.data.data.channels
})
}
}
在store/index.ts
import { TodoAction } from './actions/todos'
import { ChannelAction } from './actions/channel'
export type RootAction = TodoAction | ChannelAction
export type RootThunkAction = ThunkAction<void, RootState, any, RootAction>
在store/reducers/index.ts
import { Channel, ChannelAction } from '../actions/channel'
// 提供了channel默认的数据的类型
type ChannelType = {
channelList: Channel[]
active: number
}
// 指定初始值的类型
const initValue: ChannelType = {
channelList: [],
active: 0
}
// 指定了action的类型和返回值的类型
export default function channel(
state = initValue,
action: ChannelAction
): ChannelType {
if (action.type === 'channel/getChannelList') {
return {
...state,
channelList: action.payload
}
}
return state
}
import { RootState } from '../store'
const channel = useSelector((state: RootState) => state.channel)
<ul className="catagtory">
{channel.channelList.map((item) => {
return (
<li
key={item.id}
className={channel.active === item.id ? 'select' : ''}
>
{item.name}
</li>
)
})}
</ul>
在store/actions/channel.ts
export type ChannelAction =
| {
type: 'channel/getChannelList'
payload: Channel[]
}
| {
type: 'channel/changeActive'
payload: number
}
export function changeActive(id: number): RootThunkAction {
return (dispatch) => {
dispatch({
type: 'channel/changeActive',
payload: id
})
}
}
在store/reducers/channel.ts
export default function channel(
state = initValue,
action: ChannelAction
): ChannelType {
if (action.type === 'channel/getChannelList') {
return {
...state,
channelList: action.payload
}
}
if (action.type === 'channel/changeActive') {
return {
...state,
active: action.payload
}
}
return state
}
在components/Channel.tsx组件中注册事件
<li
key={item.id}
className={channel.active === item.id ? 'select' : ''}
onClick={() => dispatch(changeActive(item.id))}
>
在store/actions/article.ts
import axios from 'axios'
import { RootThunkAction } from '..'
export function getArticleList(id: number): RootThunkAction {
return async (dispatch) => {
const res = await axios.get(
`http://geek.itheima.net/v1_0/articles?channel_id=${id}×tamp=${Date.now()}`
)
console.log(res)
}
}
在组件中components/NewsList.tsx中
export default function NewsList() {
const active = useSelector((state: RootState) => state.channel.active)
const dispatch = useDispatch()
useEffect(() => {
dispatch(getArticleList(active))
}, [dispatch, active])
在store/actions/article.ts
import axios from 'axios'
import { RootThunkAction } from '..'
export type Article = {
art_id: string
title: string
aut_id: string
comm_count: number
pubdate: string
aut_name: string
is_top: number
cover: {
type: number
images: string[]
}
}
export type ArticleAction = {
type: 'article/getArticleList'
payload: Article[]
}
export function getArticleList(id: number): RootThunkAction {
return async (dispatch) => {
const res = await axios.get(
`http://geek.itheima.net/v1_0/articles?channel_id=${id}×tamp=${Date.now()}`
)
dispatch({
type: 'article/getArticleList',
payload: res.data.data.results
})
}
}
在store/reducers/article.ts中
import { Article, ArticleAction } from '../actions/article'
type AritcleType = Article[]
const initValue: AritcleType = []
export default function article(
state = initValue,
action: ArticleAction
): AritcleType {
if (action.type === 'article/getArticleList') {
return action.payload
}
return state
}
在组件中渲染
import { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import avatar from '../assets/back.jpg'
import { RootState } from '../store'
import { getArticleList } from '../store/actions/article'
export default function NewsList() {
const active = useSelector((state: RootState) => state.channel.active)
const dispatch = useDispatch()
const articleList = useSelector((state: RootState) => state.article)
useEffect(() => {
dispatch(getArticleList(active))
}, [dispatch, active])
return (
<div className="list">
{articleList.map((item) => {
return (
<div className="article_item" key={item.art_id}>
<h3 className="van-ellipsis">{item.title}</h3>
<div className="img_box">
<img src={avatar} className="w100" alt="" />
</div>
<div className="info_box">
<span>13552285417</span>
<span>0评论</span>
<span>2018-11-29T17:02:09</span>
</div>
</div>
)
})}
</div>
)
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。