npx create-react-app@latest project-name --template typescript
//定义State规定state内部有状态数据的类型
interface State {
areas: IArea[]
albums: IAlbum[]
currentArea: number
}
//两个参数 第一个是Props(从父组件传入的数据类型),第二个参数是当前页面有状态数据的类型
class App extends React.Component<any, State> {
//对有状态数据进行初始化
state: State = {
areas: [],
albums: [],
currentArea: 1
}
//异步请求tab标签和专辑信息
async getData() {
let getareas = await request.get<IArea[]>('data/areas.json')
let getalbums = await request.get<IAlbum[]>('data/albums.json')
this.setState({
areas: getareas,
albums: getalbums,
currentArea: 1,
})
}
//执行请求的方法要写在这个方法里面
componentDidMount(): void {
this.getData()
}
render() {
const {areas,currentArea} = this.state
return <div className="app">
<Header />
<main>
<AreasTab
areas={areas}
currentId={currentArea}
tabChange={this.tabChange} />
<section className="list">
{this.currentAlbums().map((album) => {
return <AlbumItem
key={album.id}
album={album}
deleteAlbum={this.deleteAlbum}
/>
})
}
</section>
</main>
</div >
}
}
ReactDOM.render(<App />, document.getElementById('app'))
type ButtonType = 'default' | 'primary'
interface Props {
type?: ButtonType
onClick?: () => void
}
export default class Button extends React.Component<Props> {
static defaultProps = {
type:'default'
}
render() {
const {type,onClick} = this.props
let btnclassname = `button button-${type}`
return <button className = {btnclassname}>
{this.props.children}
</button>
}
}
npm add react-router-dom@6
BrowerRouter

Link(还有NavLink)

Routes

Route

点击首页和关于分别导航到Home和About页面

使用<Outlet/>指定二级路由出口,相当于vue中的router-view
在一级路由下的某个页面再嵌套几个路由
index代表默认路由
import * as React from 'react'
import { BrowserRouter, Route, Routes } from 'react-router-dom'
import Article from '../Article'
import Detail from '../Detail'
import Main from '../Main'
import Consult from '../Consult'
export default class App extends React.Component {
render() {
return (
<BrowserRouter>
//一级路由
<Routes>
<Route path='/' element={<Main/>} >
//二级路由(index代表默认路由),再Main页面下又有两个页面
<Route index element={<Article/>}/>
<Route path='article' element={<Consult/>}/>
</Route>
<Route path='/detail' element={<Detail/>} />
</Routes>
</BrowserRouter>
)
}
}
Main页面下
import React from 'react'
import { Outlet } from 'react-router-dom'
import './style.css'
export default class Main extends React.Component {
render(): React.ReactNode {
return <React.Fragment>
<div className="page">
<header className="top">
<nav className="tabs">
<ul>
<NavLink to='/'>好文精选</NavLink>
<NavLink to='/article'>热门资讯</NavLink>
</ul>
</nav>
</header>
//指定二级路由出口,相当于vue中的router-view
<Outlet />
</div>
</React.Fragment>
}
}
导入useNavigate钩子函数
import { useNavigate } from 'react-router-dom'
执行钩子函数得到转跳函数
const navigate = useNavigate()
执行转跳函数完成转跳
navigate('/about')
如果转跳时不想添加历史,可以额外添加replace为true
navigate('/about',{replace:true})
导包
import { useSearchParams } from 'react-router-dom'
传参
参数为静态的话
navigate('/detail?id=1001')
参数为动态的话
navigate(`/detail?id=${id}`)
取参数
let [params] = useSearchParams()
let id = params.get('id')

得到的链接路径为

这种方法传参在写path时需要配合id=/:id
导包
import { useNavigate, useParams } from 'react-router-dom'
传参
参数为静态的话
navigate('/detail/id=1001')
参数为动态的话
navigate(`/detail/id=${id}`)
取参数
let params = useParams()
let id = params.id

得到的链接路径为:

import React, { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
export default function Article() {
const navigate = useNavigate()
const toDetail = (id: String) => {
navigate(`/detail?id=${id}`)
}
return <React.Fragment>
</main>
</React.Fragment>
}
navigate(-1)
import { useNavigate } from 'react-router-dom'
function Detail() {
const navigate = useNavigate()
const goBack = () => {
navigate(-1)
}
return <React.Fragment>
</React.Fragment>
}
export default Detail
当所有路径都不匹配时转跳到添加 * 的路由
<Route path='*' element={<NoFound/>}><Route/>
为函数组件提供状态
import React from 'react'
import { useEffect, useState } from 'react'
function Detail() {
//第一个是属性,第二个是改变属性值的方法,number对属性类型进行限定,0表示初始值
const [count, setCount] = useState<number>(0)
return <React.Fragment>
return (
<button onClick={() => { setCount(count + 1) }}>{count}</button>
)
</React.Fragment>
}
export default Detail
如果参数的初始值需要计算的话,可以写成如下形式
const [count, setCount] = useState<number>(() => {
return 计算后的值(这个回调函数只会在组件初始化时执行一次)
}
)
副作用函数,副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)
常见的副作用
组件首次渲染执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行
- 组件初始渲染
- 组件更新 (不管是哪个状态引起的更新)
import { useEffect, useState } from 'react'
function App() {
const [count, setCount] = useState(0)
useEffect(()=>{
// dom操作
document.title = `当前已点击了${count}次`
})
return (
<button onClick={() => { setCount(count + 1) }}>{count}</button>
)
}
export default App
组件只在首次渲染时执行一次
import React, { useEffect,useState } from 'react'
import { news } from '../../services/api'
import { IArticle } from '../../type/types'
function Consult() {
const [goodmessagelist, setGoodmeaasgelist] = useState<IArticle[]>([])
useEffect(() => {
const getData = async () => {
let res = await news()
setGoodmeaasgelist(res.rows)
}
getData()
}, [])
return <React.Fragment>
</React.Fragment>
}
export default Consult
副作用函数在首次渲染时执行,在依赖项发生变化时重新执行
import React from 'react'
import { useEffect, useState } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { detail } from '../../services/api'
import { IArticle } from '../../type/types'
import './style.css'
function Detail() {
const [aritcle, setAritcle] = useState<IArticle>()
let [params] = useSearchParams()
useEffect(() => {
const getData = async () => {
let id = params.get('id')
if (id !== null) {
let res = await detail(id)
console.log(res)
setAritcle(res.data)
}
}
getData()
}, [params])
return <React.Fragment>
</React.Fragment>
}
export default Detail
在函数组件中获取真实的dom元素对象或者是组件对象
useRef 函数useRef 函数并传入null,返回值为一个对象 内部有一个current属性存放拿到的dom对象(组件实例)获取dom
import { useEffect, useRef } from 'react'
function App() {
const h1Ref = useRef(null)
useEffect(() => {
console.log(h1Ref)
},[])
return (
<div>
<h1 ref={ h1Ref }>this is h1</h1>
</div>
)
}
export default App

获取组件实例
函数组件由于没有实例,不能使用ref获取,如果想获取组件实例,必须是类组件
Foo.js
class Foo extends React.Component {
sayHi = () => {
console.log('say hi')
}
render(){
return <div>Foo</div>
}
}
export default Foo
App.js
import { useEffect, useRef } from 'react'
import Foo from './Foo'
function App() {
const h1Foo = useRef(null)
useEffect(() => {
console.log(h1Foo)
}, [])
return (
<div> <Foo ref={ h1Foo } /></div>
)
}
export default App

createContext 创建Context对象Provider 提供数据useContext函数获取数据代码实现
import { createContext, useContext } from 'react'
// 创建Context对象
const Context = createContext()
function Foo() {
return <div>Foo <Bar/></div>
}
function Bar() {
// 底层组件通过useContext函数获取数据
const name = useContext(Context)
return <div>Bar {name}</div>
}
function App() {
return (
// 顶层组件通过Provider 提供数据
<Context.Provider value={'this is name'}>
<div><Foo/></div>
</Context.Provider>
)
}
export default App

参考:react中使用css module
主要注意三点:
