useState基础使用
// 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
状态不可变
修改对象状态
规则:对于对象类型的状态变量,应该始终传给set方法一个全新的对象来进行修改
...form解构的目的是形成一个新的数组
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
组件的样式处理
案例:B站评论
渲染评论列表
受控表单绑定
// 受控绑定表单
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
// 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
组件通信
父传子-基础实现
// 父传子
// 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
父子组件通信-子传父
// 核心:在子组件中调用父组件中的函数并传递实参
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
兄弟组件通信
// 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机制跨层级组件通信
// 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 的使用
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 依赖项参数说明
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 — 清除副作用
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实现
// 封装自定义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使用规则
ReactRouter - 快速开始
创建路由开发环境
ReactRouter - 抽象路由模块
什么是路由导航
声明式导航
编程式导航
ReactRouter - 导航传参
路由导航传参
param传参需要占位
嵌套路由配置
嵌套路由配置
默认二级路由
404路由配置
ReactRouter - 俩种路由模式
Class API(编写类组件)
// 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
类组件的生命周期函数
// 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
类组件的组件通信
// 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