Ch03 Function

5/15/2022 react

# Function

函数名称大写就认为他是一个构造函数

在App.jsx中写

export default function App(){
    return <h2>123</h2>
}
1
2
3
  • 函数式组件没有生命周期
  • 函数式组件没有this
  • 函数式组件没有state状态

# Hooks(钩子 生命周期钩子)

  • react官方提供的hook
  • hook函数必须写在组件最顶层
  • 开发人员自定义的hook
/**
 * 函数式组件没有生命周期
 * 【vue生命周期】: mounted(数据请求) updated(检测数据更新) beforeDestory(垃圾回收)
 *  使用useEffect检测更新
 */
1
2
3
4
5

# usestate

这样子是无法修改视图的,所以要使用hook

修改前:

let test = "hello world"

export default function App(){
    const fn = () => {
        test = "BUBU!"
    }
    return (
        <div>
            <h2>{test}</h2>
            <button onClick={fn}>press</button>
        </div>
    )
}
1
2
3
4
5
6
7
8
9
10
11
12
13

修改后:

import { useState } from "react"

export default function App(){
    //hook函数必须写在函数头部
    const [msg, setMsg] = useState("Hello world!")
    
    const fn = () => {
        setMsg("BUBU!")
    }
    return (
        <div>
            <h2>{msg}</h2>
            <button onClick={fn}>press</button>
        </div>
    )
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# useEffect

import { useState,useEffect } from "react"

export default function App(){
    //hook函数必须写在函数头部
    const [msg1, setMsg1] = useState(1)
    const [msg2, setMsg2] = useState(1)

    /**
     * 检测数据更新,当数据更新时,执行函数中的内容
     * 一般用来发ajax请求
     * 第一个参数是函数,第二个参数表示监听的那个数据,用数组的形式,可以写多个
     * 当检测的是页面中所有的变量,可以把所有变量填写到数组中,也可以省略第二个参数
     * 当不想检测页面中任何数据的更新,可以直接给一个空数组
     */

    useEffect(() => {
        console.log("msg1更新了")
    },[msg1])
    
     //模拟beforeDestory,一般在这里做垃圾回收或者处理脏数据,比如在App根标签被修改之后
    useEffect(() => {
    useEffect(() => {
        return () => console.log("销毁阶段")
    })

    return (
        <div>
            <h2>{msg1}</h2>
            <button onClick={()=> setMsg1(msg1 + 1)}>add1</button>
            <hr/>
            <h2>{msg2}</h2>
            <button onClick={()=> setMsg2(msg2 + 1)}>add2</button>
        </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
26
27
28
29
30
31
32
33
34
35
36

# 父子组件传递参数:

父传子

import React from 'react'


//顶级组件
export default function App1() {
  return (
    <Parent/>
  )
}

//父组件
function Parent(){
    return (
        <Child num={123}/>
    )
}

//子组件
function Child(props){
    return(
        <h1>BUBU!{props.num}</h1>
    )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

子传父:

  • 在父组件中定义函数,把函数传到子组件中,再在子组件中使用
  • 无论是vue,小程序,还是react,都是这样,真正干活的是父组件
import React, {useState} from 'react'


//顶级组件
export default function App1() {
  const [msg, setMsg] = useState(66666)
  const changeNum = (arg) => setMsg(arg)
  return (
    <Parent msg = {msg}  changeNum = {changeNum}/>
  )
}

//父组件
function Parent(props){
    return (
        <Child msg = {props.msg}   changeNum = {props.changeNum}/>
    )
}

//子组件
function Child(props){
    return(
      <div>
        <h1>BUBU!{props.msg}</h1>
        <button onClick={()=>props.changeNum(888)}>change</button>
      <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
26
27
28

但是多级组件会非常麻烦,不合理:

# Context: 使用上下文+Provider和consumer表示

import React, {useState, createContext} from 'react'

//创建上下文空间
  const numContext = createContext()
//顶级组件
export default function App4() {
  const [msg, setMsg] = useState(66666)
  const changeNum = (arg) => setMsg(arg)

  return (
    //   num和setMsg的提供者,第一个大括号表示要写js代码,第二个花括号表示传递的是一个对象
      <numContext.Provider value={{msg, changeNum}}>
          <Parent/>
      </numContext.Provider>
  )
}

//父组件
function Parent(){
    return (
        <Child/>
    )
}

//子组件
function Child(){
    return(
      <numContext.Consumer>
          {
            //   接受参数
            ({msg, changeNum}) =>(
                <div>
                    <h1>BUBU!{msg}</h1>
                    <button onClick={()=>changeNum(555)}>change</button>
                <div/>
            )
        }
          
        
      </numContext.Consumer> 
    )
}
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

# 也可以使用useContext直接解构属性, 修改如下:

import React, {useState, createContext, useContext} from 'react'

//创建上下文空间
  const numContext = createContext()
//子组件
function Child(){
    //直接使用useContex解构上下文空间
    const {msg, changeNum} = useContext(numContext)
    return(
    <>
        <h1>BUBU!{msg}</h1>
        <button onClick={()=>changeNum(666)}>change</button>
    </>
    )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 受控组件和不受控组件

  • 只存在于表单元素
  • 受控组件的意思就是表单元素的值要通过state来定义(e表示event)
  • 不受控组件的意思就是表单元素只能通过useRef来获取
import React,{useState, useRef} from 'react'

export default function App6() {
    const [value, setValue] = useState("hint"); 
    const input = useRef(null);
    return (
    <div>
        <h3>受控组件</h3>
        <input type='text' value={value} onChange={(e)=>setValue(e.target.value)}></input>
        <button onClick={()=>console.log(value)}>test1</button>
        <hr></hr>
        <h3>不受控组件</h3>
        <input type='text' ref={input}></input>
        <button onClick={()=>console.log(input.current.value)}>test2</button>
    </div>
  )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# memo组件:这样会导致child组件每次被点击之后都被加载(父组件的render重新渲染),很吃性能

使用前:

import React,{useState, memo} from 'react'

export default function App8() {
    const [num,setNum] = useState(1)

    return (
    <div>
        <h1>num:{num}</h1>
        <button onClick={()=>setNum(num + 1)}>add</button>
        <hr/>
        <Child/>
    </div>
  )
}

function Child(){
    console.log("child init...")
    return (
        <h1>child</h1>
    )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

使用后:改写child,把子组件缓存下来

const Child = memo(() => {
    console.log("child init...")
    return (
        <h1>child</h1>
    )
})
1
2
3
4
5
6

但是memo只能在纯静态的情况下使用,如果在子组件中调用父组件的方法,又会重新渲染:比如

import React,{useState, memo} from 'react'

export default function App8() {
    const [num,setNum] = useState(1)
    const dosth = () => setNum(num + 1)

    return (
    <div>
        <h1>num:{num}</h1>
        <button onClick={()=>setNum(num + 1)}>add</button>
        <hr/>
        <Child fn={dosth}/>
    </div>
  )
}


const Child = memo((props) => {
    console.log("child init...")
    return (
        <div>
            <h1>child</h1>
            <button onClick={props.fn}>do</button>
        </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
26

# useCallback:把dosth用useCallBack套起来,表示不检测数数字更新,但是会有问题,只能改变数字一次

const dosth = useCallback(() => setNum(num + 1),[])
1

这是因为如下原因:所以应该这样写

/**
 * setNum(newValue) 表示强行用新值覆盖旧值
 * setNum((num)=>num+1) 表示用新值覆盖旧值
 */
const dosth = useCallback(() => setNum((num)=>num+1),[])
1
2
3
4
5

# usememo:和useCallback类似,只不过多加一个return,在函数中返回函数, 叫做高阶函数

 const dosss = useMemo(() => {
        return () => setNum((num)=>num+1)
    },[])
1
2
3
Last Updated: 11/19/2024, 1:54:38 PM