昨天搬家搬了一天,今天继续 b 站 react 学习计划

组件基础

组件是什么

数据和方法的简单封装

函数组件

1
2
3
4
5
6
7
8
9
10
11
12
13
function Hello() {
return <div style={{color:"red"}}>Hello World!</div>
}

function App() {
return (
<div className="App">
<Hello></Hello>
</div>
);
}

export default App;

一些说明:

  1. 组件首字母大写:react 判断组件和普通 html 标签的依据
  2. 函数组件必须有返回值,无需渲染则返回 null
  3. 函数名为组件标签名,可成对也可自闭合

类组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from "react";

class Hello extends React.Component{
render(){
return <div>Hello World!</div>
}
}

function App() {
return (
<div className="App">
<Hello />
</div>
);
}

export default App;

一些说明:

  1. 同函数组件,类组件必须大写字母开头
  2. 类组件继承自 React.Component 父类
  3. 类组件必须提供 render 方法,并必须有返回值

事件绑定

on+事件名称 = {事件处理程序}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 函数组件
function Hello{
const clickHandler = ()=>{
console.log('click')
}
return <div onClick={clickHandler}>Hello World!</div>
}
// 类组件(标准写法,避免this的指向问题)
// 这样的写法能够使得回调函数中this指向当前组件
class Hello extends React.Component {
clickHandler = () => {
console.log("click")
}
render () {
return <div onClick={this.clickHandler}>Hello World!</div>
}
}

事件对象 e 的使用

1
2
3
4
5
6
7
8
9
10
class Hello extends React.Component {
clickHandler = (e) => {
// 阻止默认行为(链接跳转)
e.preventDefault()
console.log("click", e)
}
render () {
return <div><a href="http://www.baidu.com/" onClick={this.clickHandler}>百度</a></div>
}
}

额外参数传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 传递额外参数
class Hello extends React.Component {
clickHandler = (msg) => {
console.log("click", msg)
}
render () {
return <div><a href="http://www.baidu.com/" onClick={() => this.clickHandler('Hello World')}>百度</a></div>
}
}
// 同时需要事件对象e和传递额外参数
class Hello extends React.Component {
clickHandler = (e, msg) => {
e.preventDefault()
console.log("click", msg)
}
render () {
return <div><a href="http://www.baidu.com/" onClick={(e) => this.clickHandler(e, 'Hello World')}>百度</a></div>
}
}

组件状态

在 react 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
class TestComponent extends React.Component {
state = {
// 1.定义状态
name: 'current name'
}
// 事件回调函数
changeName = () => {
// 3.状态修改
// 不能直接赋值修改,必须通过setState,这个方法是继承得到的
this.setState({
name: 'name was changed'
})
}
render () {
// 2.使用状态
return (
<div>
this is TestComponent
当前名字为{this.state.name}
<button onClick={this.changeName}>change</button>
</div>
)
}
}

类组件修改 counter

咱直接在上一段代码中修改

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 TestComponent extends React.Component {
state = {
// 1.定义状态
name: 'current name',
count: 0
}
// 事件回调函数
changeName = () => {
// 3.状态修改
// 不能直接赋值修改,必须通过setState
this.setState({
count: this.state.count + 1,
name: `name was changed ${this.state.count + 1} times`
})
}
render () {
// 2.使用状态
return (
<div>
this is TestComponent
当前名字为{this.state.name}
<button onClick={this.changeName}>change</button>
</div>
)
}
}

this 指向相关问题

如果在组件中并不使用箭头函数,而是直接使用函数的话,会出现 this 的指向问题

1
2
3
4
5
6
7
8
9
10
class Test extends React.Component {
handler(){
console.log(this)
}
render () {
return (
<button onClick={this.handler}>click to test</button>
)
}
}

如上述代码会出现 this 输出为 undefine 的问题

如果一定要采用非箭头函数的写法,有以下修正方式

  1. constructor 修正
  2. 箭头函数调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 方法一:constructor修正
// 使用bind来修正this
// 相当于在组件初始化阶段将this永远指向组件的实例对象
class Test extends React.Component {
constructor() {
super()
this.handler = this.handler.bind(this)
}
handler () {
console.log(this)
}
render () {
return (
<button onClick={this.handler}>click to test</button>
)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 方法二:箭头函数调用
// 通过箭头函数的写法,直接沿用父函数中的this指向
class Test extends React.Component {
handler () {
console.log(this)
}
render () {
return (
// 此处箭头函数的父级函数为render
// render函数中的this指向为当前组件对象(react内部修正)
<button onClick={() => this.handler()}>click to test</button>
)
}
}

表单处理

受控组件:被 react 的状态控制的组件

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 Test extends React.Component {
state = {
// 1. 声明用于控制值的react组件状态
msg: 'this is message'
}
inputChange (e) {
// 4. 拿到输入框中的值,交给state
this.setState({
msg: e.target.value
})
}
render () {
return (
// 2. 绑定state中的值
// 3. 绑定事件,拿到当前输入框中的数据
<>
<div>{this.state.msg}</div>
<input
type='text'
value={this.state.msg}
onChange={(e) => this.inputChange(e)}
/>
</>
)
}
}

非受控组件:手动操作 dom 来获取文本框的值,文本框状态不受 state 中的状态控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 1. 导入creatRef函数
import React, { createRef } from "react"

class Test extends React.Component {
// 2. 调用createRef函数,创建Ref示例属性
msgRef = createRef()
getValue = () => {
// 4. 通过msgRef.current可以拿到input对应的dom元素
console.log(this.msgRef.current.value)
}
render () {
return (
// 3. 为input添加ref属性
<>
<input
type='text'
ref={this.msgRef}
/>
<button onClick={this.getValue}>点击获取变化的值</button>
</>
)
}
}

组件通信

父传子

  1. 父组件提供需要传递的数据 state
  2. 给子组件标签添加属性值为 state 中的数据
  3. 子组件通过 props 接收父组件中传过来的值
    |—类组件使用 this.props 获取 props 对象
    |—函数式组件直接通过参数获取 props 对象
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
import React from "react"

// App 父组件 Son 自组件

// 3.通过props接收父组件传过来的值
function SonA(props){
return (
<div>SonA,{props.msg}</div>
)
}
class SonB extends React.Component{
render(){
return (
<div>SonB, {this.props.msg}</div>
)
}
}

class App extends React.Component{
// 1.准备数据
state = {
msg: 'this is message'
}
render(){
return (
<>
{/* 子组件身上绑定属性 */}
<SonA msg={this.state.msg}/>
<SonB msg={this.state.msg}/>
</>
)
}
}

export default App

props 可以传递任何数据:数字、字符串、布尔值、数组、对象、函数、JSX

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
import React from "react"

// App 父组件 Son 自组件

// 3.通过props接收父组件传过来的值
function SonA (props) {
props.getMsg()
return (
<div>SonA, {props.list.map(item =>
<div key={item}>{item}</div>
)}, {props.child}</div>
)
}
class SonB extends React.Component {
render () {
return (
<div>SonB, {this.props.msg}</div>
)
}
}

class App extends React.Component {
// 1.准备数据
state = {
msg: 'this is message',
list: [1, 2, 3]
}
getMsg = () => {
console.log('success')
}
render () {
return (
<>
{/* 子组件身上绑定属性 */}
<SonA
msg={this.state.msg}
list={this.state.list}
getMsg={this.getMsg}
child={<span>helloworld</span>}
/>
<SonB msg={this.state.msg} />
</>
)
}
}

export default App

根据单项数据流的要求,子组件只能读取 props 中的数据,不能进行修改

解构 props 可以让代码整体变得更加简单,只解构需要使用的部分即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 传入后解构
function SonA (props) {
const {list, child} = props
return (
<div>SonA, {list.map(item =>
<div key={item}>{item}</div>
)}, {child}</div>
)
}
// 在参数位置解构
function SonA ({list, child}) {
return (
<div>SonA, {list.map(item =>
<div key={item}>{item}</div>
)}, {child}</div>
)
}

子传父

子传父的本质是子组件调用父组件传递的函数,并将想要传递的数据当成函数的实参

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
import React from "react"

function SonA({getSonMsg}){
function clickHandler(){
getSonMsg('这是子组件的信息')
}
return (
<div>this is son
<button onClick={clickHandler}>click</button>
</div>
)
}

class App extends React.Component {
getSonMsg = (sonMsg) => {
console.log(sonMsg)
}
render () {
return (
<SonA getSonMsg={this.getSonMsg}/>
)
}
}

export default App

兄弟通信

兄弟通信的本质还是借助父子组件通信

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
import React from "react"

function SonA({getSonMsg}){
function clickHandler(){
getSonMsg('这是子组件A的信息')
}
return (
<div>this is son
<button onClick={clickHandler}>click</button>
</div>
)
}

function SonB({brotherMsg}){
return (
<div>{brotherMsg}</div>
)
}

class App extends React.Component {
state = {
msg:''
}
getSonMsg = (sonMsg) => {
this.setState({
msg: sonMsg
})
}
render () {
return (
<>
<SonA getSonMsg={this.getSonMsg}/>
<SonB brotherMsg={this.state.msg}/>
</>
)
}
}

export default App

Context 跨组件通信

实现从上往下跨任意层传递信息

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
// 1.导入createContext
import React, { createContext } from "react"
const { Provider, Consumer } = createContext()

function ComA () {
return (
<div>
this is ComA
<ComB />
</div>
)
}

function ComB () {
return (
// 3.通过Consumer使用数据
<div>
<Consumer>
{value => <span>{value}</span>}
</Consumer>
this is ComB
</div>
)
}

class App extends React.Component {
state = {
msg: 'this is a message'
}
render () {
return (
// 2.使用provider包裹根组件
<Provider value={this.state.msg}>
<>
<ComA />
</>
</Provider>
)
}
}

export default App

to be continued (maybe)