2023年重学 react 18

99 阅读6分钟

useState基础使用

image.png

// useState实现一个计数器按钮
import { useState } from 'react'
function App () {
  // 1. 调用useState添加一个状态变量
  // count 状态变量
  // setCount 修改状态变量的方法
  const [count, setCount] = useState(0)

  // 2. 点击事件回调
  const handleClick = () => {
    // 作用: 
    // 1. 用传入的新值修改count
    // 2. 重新使用新的count渲染UI
    setCount(count + 1)
  }
  return (
    <div>
      <button onClick={handleClick}>{count}</button>
    </div>
  )
}

export default App

状态不可变

image.png

修改对象状态

规则:对于对象类型的状态变量,应该始终传给set方法一个全新的对象来进行修改

...form解构的目的是形成一个新的数组

image.png

import { useState } from 'react'

function App () {
  let [count, setCount] = useState(0)

  const handleClick = () => {
    // 直接修改 无法引发视图更新
    // count++
    // console.log(count)
    setCount(count + 1)
  }

  // 修改对象状态
  const [form, setForm] = useState({ name: 'jack' })

  const changeForm = () => {
    // 错误写法:直接修改
    // form.name = 'john'
    // 正确写法:setFrom 传入一个全新的对象
    setForm({
      ...form,
      name: 'john'
    })
  }

  return (
    <div>
      <button onClick={handleClick}>{count}</button>
      <button onClick={changeForm}>修改form{form.name}</button>
    </div>
  )
}

export default App

组件的样式处理

image.png

案例:B站评论

image.png

渲染评论列表

image.png

受控表单绑定

image.png

// 受控绑定表单

import { useState } from "react"

// 1. 声明一个react状态 - useState

// 2. 核心绑定流程
// 1. 通过value属性绑定react状态
// 2. 绑定onChange事件 通过事件参数e拿到输入框最新的值 反向修改到react状态身上

function App () {
  const [value, setValue] = useState('')
  return (
    <div>
      <input
        value={value}
        onChange={(e) => setValue(e.target.value)}
        type="text" />
    </div>
  )
}

export default App

React中获取DOM

image.png

// React中获取DOM

import { useRef } from "react"

// 1. useRef生成ref对象 绑定到dom标签身上

// 2. dom可用时,ref.current获取dom
// 渲染完毕之后dom生成之后才可用

function App () {
  const inputRef = useRef(null)
  const showDom = () => {
    console.dir(inputRef.current)
  }
  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={showDom}>获取dom</button>
    </div>
  )
}

export default App

组件通信

image.png

父传子-基础实现

image.png

image.png

image.png

// 父传子
// 1. 父组件传递数据  子组件标签身上绑定属性
// 2. 子组件接收数据  props的参数

function Son (props) {
  // props:对象里面包含了父组件传递过来的所有的数据
  // { name:'父组件中的数据'}
  console.log(props)
  return <div>this is son, {props.name}, jsx: {props.child}</div>
}


function App () {
  const name = 'this is app name'
  return (
    <div>
      <Son
        name={name}
        age={18}
        isTrue={false}
        list={['vue', 'react']}
        obj={{ name: 'jack' }}
        cb={() => console.log(123)}
        child={<span>this is span</span>}
      />
    </div>
  )
}

export default App

父子组件通信-子传父

image.png

image.png

// 核心:在子组件中调用父组件中的函数并传递实参

import { useState } from "react"


function Son ({ onGetSonMsg }) {
  // Son组件中的数据
  const sonMsg = 'this is son msg'
  return (
    <div>
      this is Son
      <button onClick={() => onGetSonMsg(sonMsg)}>sendMsg</button>
    </div>
  )
}

function App () {
  const [msg, setMsg] = useState('')
  const getMsg = (msg) => {
    console.log(msg)
    setMsg(msg)
  }
  return (
    <div>
      this is App, {msg}
      <Son onGetSonMsg={getMsg} />
    </div>
  )
}

export default App

兄弟组件通信

image.png


// 1. 通过子传父 A -> App
// 2. 通过父传子 App -> B

import { useState } from "react"

function A ({ onGetAName }) {
  // Son组件中的数据
  const name = 'this is A name'
  return (
    <div>
      this is A compnent,
      <button onClick={() => onGetAName(name)}>send</button>
    </div>
  )
}

function B ({ name }) {
  return (
    <div>
      this is B compnent,
      {name}
    </div>
  )
}

function App () {
  const [name, setName] = useState('')
  const getAName = (name) => {
    console.log(name)
    setName(name)
  }
  return (
    <div>
      this is App
      <A onGetAName={getAName} />
      <B name={name} />
    </div>
  )
}

export default App

使用Context机制跨层级组件通信

1703385727545.png

// App -> A -> B

import { createContext, useContext } from "react"

// 1. createContext方法创建一个上下文对象

const MsgContext = createContext()

// 2. 在顶层组件 通过Provider组件提供数据

// 3. 在底层组件 通过useContext钩子函数使用数据

function A () {
  return (
    <div>
      this is A component
      <B />
    </div>
  )
}

function B () {
  const msg = useContext(MsgContext)
  return (
    <div>
      this is B compnent,{msg}
    </div>
  )
}

function App () {
  const msg = 'this is app msg'
  return (
    <div>
      <MsgContext.Provider value={msg}>
        this is App
        <A />
      </MsgContext.Provider>
    </div>
  )
}

export default App

useEffect 的使用

image.png

image.png

import { useEffect, useState } from "react"

const URL = 'http://geek.itheima.net/v1_0/channels'

function App () {
  // 创建一个状态数据
  const [list, setList] = useState([])
  useEffect(() => {
    // 额外的操作 获取频道列表
    async function getList () {
      const res = await fetch(URL)
      const jsonRes = await res.json()
      console.log(jsonRes)
      setList(jsonRes.data.channels)
    }
    getList()
  }, [])
  return (
    <div>
      this is app
      <ul>
        {list.map(item => <li key={item.id}>{item.name}</li>)}
      </ul>
    </div>
  )
}

export default App

useEffect 依赖项参数说明

image.png

import { useEffect, useState } from "react"

function App () {
  // 1. 没有依赖项  初始 + 组件更新(任何组件更新都会触发)
  const [count, setCount] = useState(0)
  // useEffect(() => {
  //   console.log('副作用函数执行了')
  // })

  // 2. 传入空数组依赖  初始执行一次
  // useEffect(() => {
  //   console.log('副作用函数执行了')
  // }, [])

  // 3. 传入特定依赖项  初始 + 依赖项变化时执行
  useEffect(() => {
    console.log('副作用函数执行了')
  }, [count])

  return (
    <div>
      this is app
      <button onClick={() => setCount(count + 1)}>+{count}</button>
    </div>
  )
}

export default App

useEffect — 清除副作用

image.png

import { useEffect, useState } from "react"

function Son () {
  // 1. 渲染时开启一个定时器
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('定时器执行中...')
    }, 1000)

    return () => {
      // 清除副作用(组件卸载时)
      clearInterval(timer)
    }
  }, [])
  return <div>this is son</div>
}

function App () {
  // 通过条件渲染模拟组件卸载
  const [show, setShow] = useState(true)
  return (
    <div>
      {show && <Son />}
      <button onClick={() => setShow(false)}>卸载Son组件</button>
    </div>
  )
}

export default App

自定义Hook实现

image.png

// 封装自定义Hook

// 问题: 布尔切换的逻辑 当前组件耦合在一起的 不方便复用

// 解决思路: 自定义hook

import { useState } from "react"

function useToggle () {
  // 可复用的逻辑代码
  const [value, setValue] = useState(true)

  const toggle = () => setValue(!value)

  // 哪些状态和回调函数需要在其他组件中使用 return
  return {
    value,
    toggle
  }
}

// 封装自定义hook通用思路

// 1. 声明一个以use打头的函数
// 2. 在函数体内封装可复用的逻辑(只要是可复用的逻辑)
// 3. 把组件中用到的状态或者回调return出去(以对象或者数组)
// 4. 在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用


function App () {
  const { value, toggle } = useToggle()
  return (
    <div>
      {value && <div>this is div</div>}
      <button onClick={toggle}>toggle</button>
    </div>
  )
}

export default App

React Hooks使用规则

image.png

ReactRouter - 快速开始

image.png

创建路由开发环境

image.png

image.png

ReactRouter - 抽象路由模块

1703402725961.png

image.png

image.png

什么是路由导航

image.png

image.png

声明式导航

image.png

编程式导航

image.png

ReactRouter - 导航传参

路由导航传参

param传参需要占位

image.png image.png

image.png

image.png

嵌套路由配置

image.png

嵌套路由配置

image.png

默认二级路由

image.png

404路由配置

image.png

ReactRouter - 俩种路由模式

image.png

Class API(编写类组件)

image.png

// Class API

import { Component } from "react"

class Counter extends Component {
  // 编写组件的逻辑代码
  // 1. 状态变量  2. 事件回调  3.UI(JSX)
  // 1. 定义状态变量
  state = {
    count: 0
  }

  // 2. 定义事件回调修改状态数据
  setCount = () => {
    // 修改状态数据
    this.setState({
      count: this.state.count + 1
    })
  }

  render () {
    return <button onClick={this.setCount}>{this.state.count}</button>
  }
}


function App () {
  return (
    <>
      <Counter />
    </>
  )
}

export default App

类组件的生命周期函数

image.png

// Class API 生命周期

import { Component, useState } from "react"

class Son extends Component {
  // 声明周期函数
  // 组件渲染完毕执行一次  发送网络请求
  componentDidMount () {
    console.log('组件渲染完毕了,请求发送起来')
    // 开启定时器
    this.timer = setInterval(() => {
      console.log('定时器运行中')
    }, 1000)
  }

  // 组件卸载的时候自动执行  副作用清理的工作 清除定时器 清除事件绑定
  componentWillUnmount () {
    console.log('组件son被卸载了')
    // 清除定时器
    clearInterval(this.timer)
  }

  render () {
    return <div>i am Son</div>
  }
}

function App () {
  const [show, setShow] = useState(true)
  return (
    <>
      {show && <Son />}
      <button onClick={() => setShow(false)}>unmount</button>
    </>
  )
}

export default App

类组件的组件通信

image.png

// Class API 父子通信

import { Component } from "react"
// 1. 父传子  直接通过prop子组件标签身上绑定父组件中的数据即可
// 2. 子传父  在子组件标签身上绑定父组件中的函数,子组件中调用这个函数传递参数

// 总结
// 1. 思想保持一致
// 2. 类组件依赖于this


// 子组件
class Son extends Component {
  render () {
    // 使用this.props.msg
    return <>
      <div>我是子组件 {this.props.msg}</div>
      <button onClick={() => this.props.onGetSonMsg('我是son组件中的数据')}>sendMsgToParent</button>
    </>
  }
}

// 父组件
class Parent extends Component {
  state = {
    msg: 'this is parent msg'
  }

  getSonMsg = (sonMsg) => {
    console.log(sonMsg)
  }

  render () {
    return <div>我是父组件<Son msg={this.state.msg} onGetSonMsg={this.getSonMsg} /></div>
  }
}


function App () {
  return (
    <>
      <Parent />
    </>
  )
}

export default App