前言

React的基本部分,包括Class与Hook的语法。


class类与对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Animal {
//构造
constructor(name) {
this.name = name;
}

run() {
return console.log(this.name + '跑')
}

//静态方法
static eat(name) {
return console.log(name + '吃') // 注意这里this.name是没有值,下面调用没有给
}
}

// 类(Animal)通过实例化(new)变为对象(pig)
const pig = new Animal('小猪');
pig.run()

// 静态方法直接通过类方法名来调用
Animal.eat('猫');

class Bird extends Animal {
constructor(name, color = '黄色') {
//super调用父类的代码
super(name);
this.color = color;
}

run() {
super.run() // 可以不覆盖原函数的方法 // 输出鹦鹉跑
return console.log(this.name + this.color) // 输出鹦鹉黄色
}
}

const polly = new Bird('鹦鹉');
polly.run();

项目命令

1
2
3
4
5
6
7
8
// 安装项目
npx create-react-app demo
// 或者
yarn create react-app demo
// Ant Design安装
yarn add antd
// 启动命令
yarn start

Class组件介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// class组件,先引用Component
import React, {Component} from 'react'
class App extends Component {
// 构造器
constructor(props) {
super(props)
this.state = {
count: 0
}
}
//
render() {
return (
// Fragments概念(或者<React.Fragment></React.Fragment>)
<>
{/* 设置和读取state里面的值 */}
<p>你点击了 {this.state.count} 次</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
点我
</button>
</>
)
}
}
// 导出App
export default App

生命周期


Class的this指向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class App extends Component {
constructor(props) {
super(props);
// 第一种:为了在回调中使用this,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this)
}
handleClick() {alert(this)}
// class fields写法
// handleClick=()=> {alert(this)}
render() {
return (
{/*这里可以直接找到handleClick*/}
{/*第二种:直接onClick={()=>{this.handleClick}};上面不做变化,相当于事件放在这里*/}
<button onClick={this.handleClick}>点这里</button>
)
}
}

父子传值props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 子组件
class Welcome extends React.Component {
render() {
return (
<h1 onClick={() => this.props.handleClick('参数')}>{this.props.name}</h1>
);
}
}
// 父组件
class App extends Component {
render() {
return (
<Welcome name="Aaron"
handleClick={(value) => {console.log(value,'Aaron')}}/>
)
}
}

条件渲染、列表循环、表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import React, {Component} from 'react'

class App extends Component {
constructor(props) {
super(props)
this.state = {show: true,data: [],name:''}
}
handleClick() {
this.setState(state => {return {show: !state.show}})
}
handleChange(value) {
this.setState({name: value.target.value})
}
handleSubmit(value) {
alert('提交的名字: ' + this.state.name)
value.preventDefault() // 防止刷新(原生方法)
}
render() {
const {data, show, name} = this.state // 取值
return (
<>
{/*条件渲染或者使用三目运算符*/} {/*{show ? <p>显示出来了</p> : ''}*/}
{show && <p>显示出来了</p>}
<button onClick={() => this.handleClick()}>点这里</button>
{/*列表循环*/}
data.map((item) =><li key={item.id.toString()}>{item.name}</li>)
{/*表单,双向绑定*/}
<form onSubmit={(value) => this.handleSubmit(value)}>
<input type="text"
value={name}
onChange={(value) => this.handleChange(value)}/>
<input type="submit" value="提交"/>
</form>
</>
)
}
}
export default App

Hook组件介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React, { useState, useEffect } from 'react';

function App() {
// 声明一个叫 "count" 的 state 变量,并定义了设置count值的方法 "setCount"
const [count, setCount] = useState(0);

// 生命周期的作用
// 读取数据函数
const fetchData = ()=>{}
useEffect(() => {
// 里面调用
fetchData();
// 下面写上[]表示执行一次,写上具体的值就表示监听
},[]);
return (
<div>
<p>你点击了 {count} 次</p>
<button onClick={() => setCount(count + 1)}>点我</button>
</div>
);
}
export default App

Hook实现搜索、加载、错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 双向绑定
const [keyword, setKeyword] = useState('');
// 请求的地址后接上keyword
<input
type="text"
value={keyword}
// 用户输入的内容,设置给state中的keyword
onChange={(value) => {
setKeyword(value.target.value)
console.log(value.target.value)
}}/>
// 加载
const [loading, setLoading] = useState(false);
// 请求开始前,loading=true
setLoading(true);
// 请求完成后,loading=false
setLoading(false);
// return里面写上
{/*根据loading的值,判断是显示加载中,或是课程列表?*/}
{loading ? (<div>加载中...</div>) : (<div>加载成功</div>)}
// 错误处理
const [error, setError] = useState(false);
// 请求开始前,error=false
setError(false);
axios(`xxx`).then(res => {}).catch(error => {
//发生了错误,设置error=true
setError(true);
}).finally(() => {setLoading(false)})}
// 判断接口读取错误
if (error) { return (<button onClick={fetchData}>立即重试</button>)}

Hook扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 使用useContext,组件之间直接传值
import {React,useContext} from 'react';
// 创建一个context对象,默认值为themes
const ThemeContext = React.createContext(themes);
function A() {
return (
// 使用ThemeContext包裹子组件
<ThemeContext.Provider value={themes.dark}>
<B/>
</ThemeContext.Provider>
);
}
function B() {return (<div><C/></div>)}
function C() {
// 使用useContext,就能直接读取到A组件中传递过来的value了
const theme = useContext(ThemeContext);
return (
<button style={{background: theme.background}}>A直接传C</button>
);
}

// 重复渲染子组件的情况
// memo-当组件的props或者state变化时,会重新渲染页面,触发页面重新渲染。防止子组件重新渲染
// 使用 React.memo()
import {memo} from 'react';
const Child = memo(() => {return (<div>子组件</div>)})
// 或者
let Child = function () {return (<div>子组件</div>)}
Child = memo(Child)

// useCallback-当子组件有父组件中的方法(props),也会重新渲染
import {useCallback} from 'react';
// 子组件中的方法写上useCallback(后页可以监听)
const handleClick = useCallback(() => {
console.log("子组件的按钮被点击了...")
}, [])

// useMemo-当传递值为对象时,再次重复渲染
// useMemo 包裹一下值
const data = useMemo(() => ({
title: '这里是子组件'
}), [])
// 第二个参数作为依赖项可以当做成计算属性使用
const fullName = useMemo(
() => lastName + firstName,
[firstName, lastName],
);

路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// 安装
yarn add history@5 react-router-dom@6
// 使用路由src/index.js文件中
import {BrowserRouter} from "react-router-dom";
// 删除严格模式(防止antdesign有报错)
<React.StrictMode></React.StrictMode> // 删除掉
// 使用路由
<BrowserRouter><App/></BrowserRouter>

// src/App.js中修改
import * as React from "react";
import { Routes, Route, Link } from "react-router-dom";
// 注意引用的组件取名首字母大写
function App() {
return (
<Routes>
<Route path="/" element={<Home/>}>
<Route path="about/:id" element={<About/>}/>
{/* 使用 path="*",所有未匹配的路由,都会到这里来这里*/}
<Route path="*" element={<NoMatch/>}/>
{/* 使用index,index路由和他的父路由路径是一样*/}
<Route index element={<Home/>}/>
</Route>
</Routes>
)
}

// 路由出口
import { Link, Outlet } from "react-router-dom";
<Outlet /> // 首页组件中写上
<Link to={`/about/${id}`}</Link>

// 获取 URL 参数
import { useParams } from "react-router-dom";
let params = useParams();
let id = params.id

// 链接激活高亮显示
import { NavLink } from "react-router-dom";
// Link修改为NavLink,使用isActive判断当前链接是否被激活
<NavLink
style={({ isActive }) => {
return { color: isActive ? "red" : "",display: "block"}
}
}
to={`/about/${id}`}>
</NavLink>

// 使用代码跳转
import {useNavigate} from "react-router-dom";
let navigate = useNavigate();
// 使用跳转
navigate("/")

// 其他属性
// useSearchParams搜索参数
import {useSearchParams} from "react-router-dom";
let [searchParams, setSearchParams] = useSearchParams();
// useLocation,可以返回path等信息,解决
import {useLocation} from "react-router-dom";
let location = useLocation();

// 懒加载(按需加载)
const About = React.lazy(() => import("xxx"));
<Route path="/about" element={
// 使用React.Suspense因为组件是懒加载,在渲染时被暂停了,
// react无法得知渲染内容,为了避免报错使用Suspense来进行空档期的显示
<React.Suspense fallback={<>空档期显示的组件</>}>
<About/>
</React.Suspense>}
/>

useReducer 统一管理状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// 引用 useReducer
import {useEffect, useReducer} from 'react';
import axios from 'axios';

// 初始状态
// Data是初始值,如果用户不传,默认就是 []
let initialState = {
loading: false,
error: false,
data: [],
}
// 定义reducer,统一管理状态
const reducer = (state, action) => {
switch (action.type) {
case 'init':
return {
...state,
loading: true,
error: false
};
case 'success':
return {
...state,
loading: false,
error: false,
data: action.payload
};
case 'failure':
return {
...state,
loading: false,
error: true
};
default:
throw new Error();
}
};

// 自定义hook
const useFetchData = (url, initData) => {
// 如果有传过来的initData,设置到initialState里
initialState = {
...initialState,
data: initData || [],
}
// 使用useReducer初始化数据
const [state, dispatch] = useReducer(reducer, initialState);
// 接口请求
const fetchData = url => {
dispatch({type: 'init'});

axios(url).then(res => {
// 成功的状态
// 注意封装出去有四次请求,刷新开始为空;
// 在页面上有data.data出现undefined;后还需data.data.xxx时会报错;
// 解决:在这里写上res.data.data,或者在那边将initData设置为{data:{},xxx:[]}等;
dispatch({type: 'success', payload: res.data.data});
}).catch(error => {
// 失败的状态
dispatch({type: 'failure'});
})
}
// 监听 url 参数,也就说当接口地址变化后,会重新请求接口
useEffect(() => {
fetchData(url);
}, [url]);

// ...state解出来,就是loading、error和data
// 返回这些内容,在调用的页面中可以读取、调用,或再次进行设置
return {...state, fetchData};
}

export default useFetchData;

项目中的一些问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// antd安装
yarn create react-app antd-demo

// class的生命周期不用解决this,其他需要
componentDidMount() { this.handleClick() }
handleClick() {
console.log(this) // 可以打印
}

// onClick事件调用方法是否跟()
handleClick() {console.log(1)}
// 进来就调用
// 声明时就直接执行了(因而加载页面自动执行)
// 执行后的返回值赋给了onClick属性(返回值必然不会是个函数,因而再点击没有作用)
<button onClick={this.handleClick()}>点这里</button>
// 点击后调用
<button onClick={() => this.handleClick()}>点这里</button>

// 注意setState是异步操作的

// 刷新页面
window.location.reload()

// 视频标签
<video src=11111 controls />video.js

// 注意key(循环和表格)
<Table rowKey="id"></Table>
// 样式要写成{{}}, 里面对象形式,或者取名id,在APP.css里面写