黑马React18: 基础Part II

黑马React: 基础2

Date: November 16, 2023
Sum: 受控表单绑定、获取DOM、组件通信、useEffect、Hook、优化B站评论




受控表单绑定

受控表单绑定

概念:使用React组件的状态(useState)控制表单的状态

Untitled

  1. 准备一个React状态值
const [value, setValue] = useState('')
  1. 通过value属性绑定状态,通过onChange属性绑定状态同步的函数
<inputtype="text"value={value}onChange={(e) => setValue(e.target.value)}
/>

Case:

Code:

// 受控绑定表单import { useState } from "react"// 1. 声明一个react状态 - useState// 2. 核心绑定流程// 1. 通过value属性绑定 react 状态// 2. 绑定 onChange 事件 通过事件参数e拿到输入框最新的值 反向修改到react状态function App() {const [value, setValue] = useState('')return (<div className="App"><input type="text" value={value}onChange={(e) => setValue(e.target.value)}/></div>)
}export default App

Res:

Untitled




React中获取DOM

在 React 组件中获取/操作 DOM,需要使用 useRef React Hook钩子函数,分为两步:

  1. 使用useRef创建 ref 对象,并与 JSX 绑定
const inputRef = useRef(null)
<input type="text" ref={inputRef} />
  1. 在DOM可用时,通过 inputRef.current 拿到 DOM 对象
console.log(inputRef.current)

Case:

Code:

import React, { useRef } from "react"
// React中获取DOM// 1. useRef生成ref对象, 绑定到dom标签身上
// 2. dom可用时, ref.current获取dom对象// 渲染完毕之后dom生成之后可用function App() {const inputRef = useRef(null)const showDom = () => {console.log(inputRef.current)}return (<div className="App"><input type="text" ref={inputRef}/><button onClick={ () => showDom() }>获取DOM</button></div>)
}export default App

Res:

Untitled




案例:B站评论—发表评论

B站评论案例 —— 核心功能实现

Untitled

  1. 获取评论内容
  2. 点击发布按钮发布评论

Code:

  1. 获取评论内容
const [content, setContent] = useState('')...{/* 评论框 */}
<textareaclassName="reply-box-textarea"placeholder="发一条友善的评论"ref={inputRef}value={content}onChange={(e) => setContent(e.target.value)}
/>
  1. 点击发布按钮发布评论
const handlPublish = () => {setCommentList([...commentList,{rpid: uuidV4(), // 随机iduser: {uid: '30009257',avatar,uname: '黑马前端',},content: content,ctime: dayjs(new Date()).format('MM-DD hh:mm'), // 格式化 月-日 时:分like: 66,}])// 1. 清空输入框的内容setContent('')// 2. 重新聚焦  dom(useRef) - focusinputRef.current.focus()
}...<div className="reply-box-send"><div className="send-text" onClick={handlPublish}>发布</div>
</div>


B站评论案例 — id处理和时间处理

Untitled

  1. rpid要求一个唯一的随机数id - uuid
  2. ctime要求以当前时间为标准,生成固定格式 - dayjs

Code:

import { v4 as uuidV4 } from 'uuid'
import dayjs from 'dayjs'...{rpid: uuidV4(), // 随机iduser: {uid: '30009257',avatar,uname: '黑马前端',},content: content,ctime: dayjs(new Date()).format('MM-DD hh:mm'), // 格式化 月-日 时:分like: 66,
}

理解:

1-uuid会生成一个随机数

在component中查看:

Untitled

2-日期格式化

dayjs.format() // 具体参考以下文档

Ref:

随机数uuid: https://github.com/uuidjs/uuid

日期dayjs: https://dayjs.gitee.io/zh-CN/



B站评论案例 — 清空内容并重新聚焦

Untitled

  1. 清空内容 - 把控制input框的value状态设置为空串
  2. 重新聚焦 - 拿到input的dom元素,调用focus方法

Code:

const handlPublish = () => {setCommentList([...commentList,{rpid: uuidV4(), // 随机iduser: {uid: '30009257',avatar,uname: '黑马前端',},content: content,ctime: dayjs(new Date()).format('MM-DD hh:mm'), // 格式化 月-日 时:分like: 66,}])// 1. 清空输入框的内容setContent('')// 2. 重新聚焦  dom(useRef) - focusinputRef.current.focus()
}...{/* 评论框 */}
<textareaclassName="reply-box-textarea"placeholder="发一条友善的评论"ref={inputRef} // 给评论框绑定个ref标签, 方便获取value={content}onChange={(e) => setContent(e.target.value)}
/>



组件通信

理解组件通信

概念:组件通信就是组件之间的数据传递,根据组件嵌套关系的不同,有不同的通信方法

Untitled



父传子-基础实现

Untitled

实现步骤:

  1. 父组件传递数据 - 在子组件标签上绑定属性
  2. 子组件接收数据 - 子组件通过props参数接收数据

Case:

Code:

// 父传子
// 1. 父组件传递数据 子组件标签身上绑定属性
// 2. 子组件接受数据 props的参数function Son(props) {console.log(props);return <div>this is Son, {props.name}</div>
}function App() {const name = 'this is app name'return (<div className="App"><Son name={name} /></div>)
}export default App

Res:

Untitled



父传子-props说明

  1. props可传递任意的数据

数字、字符串、布尔值、数组、对象、函数、JSX

Untitled

  1. props是只读对象

子组件只能读取props中的数据,不能直接进行修改, 父组件的数据只能由父组件修改



父传子 - 特殊的prop children

场景:当我们把内容嵌套在子组件标签中时,父组件会自动在名为children的prop属性中接收该内容

Case:

Code:

// 父传子
// 1. 父组件传递数据 子组件标签身上绑定属性
// 2. 子组件接受数据 props的参数function Son(props) {console.log(props);return <div>this is Son, {props.children}</div>
}function App() {return (<div><Son><span>this is span</span></Son></div>)
}export default App

Res:

Untitled



父子组件通信-子传父

Untitled

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

Untitled

Case:

Code:

// 子传父
// 核心: 在子组件中调用父组件中的函数并传递实参import { useState } from "react";function Son({onGetMsg}) {// Son组件中的数据const sonMsg = 'this is son msg'return (<div><button onClick={() => onGetMsg(sonMsg)}>sendMsg</button></div>)
}function App() {const [msg, setMsg] = useState('')const getMsg = (msg) => {console.log(msg);setMsg(msg)}return (<div>Father: { msg }<Son onGetMsg={getMsg}/></div>)
}export default App

Res:

Untitled



使用状态提升实现兄弟组件通信

实现流程:

Untitled

实现思路:借助“状态提升”机制,通过父组件进行兄弟组件之间的数据传递

  1. A组件先通过子传父的方式把数据传给父组件App
  2. App拿到数据后通过父传子的方式再传递给B组件

Case:

// 兄弟组件传递// 1. 通过子传父 A => App ✅
// 2. 通过父传子 App => B import { useState } from "react";function A({ onGetAName }) {const name = 'this is A name'return (<div>this is A component: <button onClick={() => onGetAName(name)}>send</button></div>)  
}function B(props) {return (<div>this is B component: {props.name}</div>)
}function App() {const [name, setName] = useState('')const getAName = (name) => {setName(name)}return (<div><h1>Father: </h1><A onGetAName={getAName} /><B name={name} /></div>)
}export default App

Res:

Untitled



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

图示:

Untitled

实现步骤

  1. 使用createContext方法创建一个上下文对象Ctx
  2. 在顶层组件(App)中通过 Ctx.Provider 组件提供数据
  3. 在底层组件(B)中通过 useContext 钩子函数获取消费数据

Untitled

Case:
Code:

// Context跨层通信
// App => A => B// 1. createContext方法创建一个上下文对象// 2. 在顶层组件 通过Provider组件提供数据
// 3. 在底层组件 通过useContext钩子函数使用数据import { createContext, useContext } from "react";const MsgContext = createContext()function A() {return (<div>this is A component< B /></div>)
}function B() {const msg = useContext(MsgContext)return (<div>this is B component - { 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

Res:

Untitled




useEffect 的使用

useEffect 的概念理解

useEffect是一个React Hook函数,用于在React组件中创建不是由事件引起而是由渲染本身引起的操作(副作用),比如发送AJAX请求,更改DOM等等

Untitled

说明:上面的组件中没有发生任何的用户事件,组件渲染完毕之后就需要和服务器要数据,整个过程属于 “只由渲染引起的操作”



useEffect 的基础使用

需求:在组件渲染完毕之后,立刻从服务端获取频道列表数据并显示到页面中

语法

useEffect(() => {}, [])

参数1是一个函数,可以把它叫做副作用函数,在函数内部可以放置要执行的操作

参数2是一个数组(可选参),在数组里放置依赖项,不同依赖项会影响第一个参数函数的执行,当是一个空数组的时候,副作用函数只会在组件渲染完毕之后执行一次

接口地址:http://geek.itheima.net/v1_0/channels

Case:
Code:

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(list);setList(jsonRes.data.channels)}getList()}, [])return (<div className="App">{/* this is app - { list[0].name } */}<ul>{ list.map(item => {return <li key={ item.id }>{ item.name }</li>})}</ul></div>)
}export default App

Res:

Untitled



useEffect 依赖项参数说明

useEffect副作用函数的执行时机存在多种情况,根据传入依赖项的不同,会有不同的执行表现

依赖项副作用函数执行时机
没有依赖项目数组初始渲染+组件更新时执行
空数组依赖只在初始渲染时执行一次
添加特定依赖项组件初始渲染 + 特性依赖项变化时执行

Case: 1. 没有依赖项

func: 点击按钮, 数字会逐渐加1

Code:

import { useEffect, useState } from "react"function App() {// 1. 没有依赖项 初始 + 组件 更新const [count, setCount] = useState(0)useEffect(() => {console.log('副作用函数执行了');})const handleAdd = () => {setCount(count + 1)}return (<div className="App">this is app<button onClick={ () => handleAdd() }> {count} </button></div>)
}export default App

Res:

Untitled

Case2: 空数组依赖

Code:

import { useEffect, useState } from "react"function App() {// 1. 没有依赖项 初始 + 组件 更新const [count, setCount] = useState(0)useEffect(() => {console.log('副作用函数执行了');})const handleAdd = () => {setCount(count + 1)}return (<div className="App">this is app<button onClick={ () => handleAdd() }> {count} </button></div>)
}export default App

Res:

Untitled

Case3: 添加特定依赖项

Code:

import { useEffect, useState } from "react"function App() {// 3. 添加特定依赖项// 依赖于 count , 只要count变化, 就执行副作用函数const [count, setCount] = useState(0)useEffect(() => {console.log('副作用函数执行了');}, [count])const handleAdd = () => {setCount(count + 1)}return (<div className="App">this is app<button onClick={ () => handleAdd() }> {count} </button></div>)
}export default App

Res:

Untitled



useEffect — 清除副作用

在useEffect中编写的由渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,比如在useEffect中开启了一个定时器,我们想在组件卸载时把这个定时器再清理掉,这个过程就是清理副作用

useEffect(() => {// 实现副作用操作逻辑return () => {// 清除副作用逻辑...}
}, [])

说明:清除副作用的函数最常见的执行时机是在组件卸载时自动执行

需求:在Son组件渲染时开启一个定制器,卸载时清除这个定时器

Case:

func: 点击卸载, 定时器会停止

Code:

import { useEffect, useState } from "react";function Son() {useEffect(() => {const timer = setInterval(() => {console.log('定时器执行中...pilipala');}, 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

Res:

Untitled




自定义Hook实现

自定义Hook函数

概念:自定义Hook是以 use 打头的函数,通过自定义Hook函数可以用来实现逻辑的封装和复用

Untitled

Case:

func: 切换 显示/隐藏 组件

Code:

// 封装自定义 Hook// 问题: 布尔切换的逻辑 当前组件耦合在一块时, 不方便复用
// 解决思路: 自定义Hookimport { useState } from "react"function useToggle() {// 可复用逻辑代码const [value, setValue] = useState(true)const handleToggle = () => {setValue(false)}// 哪些状态和回调函数需要在其他组件中使用 returnreturn {value, handleToggle}
}// 封装自定义hook通用思路
// 1. 声明一个以use打头的函数
// 2. 在函数体内封装可复用的逻辑 (只要是可复用的逻辑)
// 3. 把组件中用到的状态或者回调returan出去(以对象或者数组)
// 4. 在哪个组件中要用到这个逻辑, 就调用这个自定义hook, 结构出来状态和回调进行使用function App() {const { value, handleToggle } = useToggle()return (<div className="App">{ value && <div>this is div</div>}<button onClick={() => handleToggle()}>toggle</button></div>)
}export default App

Res:

Untitled




React Hooks使用规则

使用规则:

  1. 只能在组件中或者其他自定义Hook函数中调用

  2. 只能在组件的顶层调用,不能嵌套在 if、for、其他函数中

    理解: 就是Hooks不能有条件地执行, 只能直接在顶部采用const { value, handleToggle } = useToggle() 这种方式, 直接调用

Untitled




案例:优化B站评论案例

优化需求

Untitled

  1. 使用请求接口的方式获取评论列表并渲染
  2. 使用自定义Hook函数封装数据请求的逻辑
  3. 把评论中的每一项抽象成一个独立的组件实现渲染


优化需求-通过接口获取评论列表

  1. 使用 json-server 工具模拟接口服务, 通过 axios 发送接口请求

    json-server是一个快速以.json文件作为数据源模拟接口服务的工具
    axios是一个广泛使用的前端请求库

# -D 指安装到开发时依赖
npm i json-server -D
  1. 使用 useEffect 调用接口获取数据
useEffect(() => {// 发送网络请求...
}, [])

拓展:

json-server

使用步骤:

1-安装(参考视频)

2-配置 package.json

"scripts": {"start": "react-scripts start","build": "react-scripts build","test": "react-scripts test","eject": "react-scripts eject","serve": "json-server db.json --port 3004" // 添加 json-server 的端口
},

参考:

json-server: https://github.com/typicode/json-server



优化需求-自定义Hook函数封装数据请求

一般思路:

  1. 编写一个 use 打头的函数
  2. 函数内部编写封装的逻辑
  3. return出去组件中用到的状态和方法
  4. 组件中调用函数解构赋值使用
function useGetList () {// 获取接口数据渲染const [commentList, setCommentList] = useState([])useEffect(() => {// 请求数据async function getList () {// axios请求数据const res = await axios.get(' http://localhost:3004/list')setCommentList(res.data)}getList()}, [])return {commentList,setCommentList}
}...const [commetList, setCommetList] = useGetList()


优化需求-封装评论项Item组件

Untitled

抽象原则:App作为“智能组件”负责数据的获取,Item作为“UI组件”负责数据的渲染

// 封装Item组件
function Item ({ item, onDel }) {return (<div className="reply-item">{/* 头像 */}<div className="root-reply-avatar"><div className="bili-avatar"><imgclassName="bili-avatar-img"alt=""src={item.user.avatar}/></div></div><div className="content-wrap">{/* 用户名 */}<div className="user-info"><div className="user-name">{item.user.uname}</div></div>{/* 评论内容 */}<div className="root-reply"><span className="reply-content">{item.content}</span><div className="reply-info">{/* 评论时间 */}<span className="reply-time">{item.ctime}</span>{/* 评论数量 */}<span className="reply-time">点赞数:{item.like}</span>{/* 条件:user.id === item.user.id */}{user.uid === item.user.uid &&<span className="delete-btn" onClick={() => onDel(item.rpid)}>删除</span>}</div></div></div></div>)
}{/* 评论列表 */}
<div className="reply-list">{/* 评论项 */}{commetList.map(item => <Item key={item.rpid} item={item} onDel={() => handleDel(item.rpid)}/>)}
</div>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/200099.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

万界星空科技QMS质量管理系统功能

QMS质量管理系统结合质量决策、综合质量管理、过程质量控制三个层次要素&#xff0c;帮助企业实现产品全寿命周期质量数据的及时、灵活、准确和全面采集。 通过质量管理软件能够实现质量数据科学处理和应用&#xff0c;包括数据的系统化组织、结构化存贮、便捷式查询、定制化统…

不夸张地说,这是我见过最通俗易懂的,pytest入门基础教程!

Pytest简介 Pytest is a mature full-featured Python testing tool that helps you write better programs.The pytest framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries. 通过官方网站介绍…

京东大数据(京东数据采集):2023年Q3线上投影仪品类销售数据分析报告

11月初&#xff0c;某知名投影仪企业发布了2023年三季度财报。数据显示&#xff0c;今年第三季度&#xff0c;公司营收依然不客观&#xff0c;连续第五个季度业绩持续下滑。 从鲸参谋数据也可以看出&#xff0c;今年Q3&#xff0c;京东平台上该品牌的销量环比下滑约35%&#x…

OpenAI宫斗,尘埃落定,微软成最大赢家

周末被OpenAI董事会闹剧刷屏,ChatGPT之父Sam Altman前一天被踢出董事会,免职CEO,后一天重返OpenAI,目前结局未知。 很多同学想要围观,缺少背景知识,这里老章为大家简单介绍前因后果及涉及的人物,时间线,让大家轻松围观。 备好瓜子,开始。 1、主角 先看一张图,看一…

flink 查看写入starrocks的数据量 总行数

针对该connector: https://github.com/StarRocks/docs.zh-cn/blob/main/loading/Flink-connector-starrocks.md

深入解析Windows操作系统——概念和工具

文章目录 Windows操作系统的版本Windows NT和Windows 95基础概念和术语内核调试用户模式调试 Windows操作系统的版本 Windows NT和Windows 95 Windows NT和Windows 95之间的一些结构性差异&#xff0c;以及Windows NT优于Windows 95及其后续版本的一些方面&#xff1a; Wind…

机器学习:十大算法快速回顾

一、说明 对于机器学习的是个经典算法&#xff0c;本篇将展示一个回顾&#xff0c;注意&#xff0c;本篇不是具体原理信息介绍&#xff0c;没有代码&#xff0c;但是对于初学者是一个有益的导读。 二. 线性回归 2.1 算法描述 有没有想过数据奇才如何预测未来&#xff1f;输入线…

react 手机端 rc-table列隐藏(根据相关条件是否隐藏)、实现图片上传操作

最近公司某一项目的手机端&#xff0c;新增需求&#xff1a;table中的附件要可以编辑&#xff0c;并且是在特定条件下可编辑&#xff0c;其他仅做展示效果。 查阅官方文档&#xff0c;没有发现是否隐藏这一属性&#xff0c;通过css控制样式感觉也比较麻烦&#xff0c;后面发现可…

全球首款容器计算产品重磅发布,激活上云用云新范式

云布道师 10 月 31 日&#xff0c;杭州云栖大会上&#xff0c;阿里云云原生应用平台负责人丁宇宣布&#xff0c;阿里云容器计算服务 ACS 正式发布&#xff01;ACS 将大幅降低企业和开发者用云门槛&#xff0c;真正将 Serverless 理念大规模落地。 容器计算服务 ACS&#xff0c…

hologres 索引与查询优化

hologres 优化部分 1 hologres 建表优化1.1 建表中的配置优化1.1 字典索引 dictionary_encoding_columns1.2 位图索引 bitmap_columns1.2.2 Bitmap和Clustering Key的区别 1.3 聚簇索引Clustering Key 1 hologres 建表优化 1.1 建表中的配置优化 根据 holo的 存储引擎部分的知…

关于在3dsmax中制作的模型导入UE后尺寸大小不对的问题

现象 在3dsmax中的基本单位为毫米 在UE中基本单位是厘米 我在3dsmax中创建一个长宽高均为1000mm的方块 然后导入到UE中的世界坐标原点 方块向X轴正方向移动100个单位100cm1000mm&#xff0c;按理来说&#xff0c;新方块的此时应该和旧方块是贴着的&#xff0c;但是现象确是两者…

深度学习入门:自建数据集完成花鸟二分类任务

自建数据集完成二分类任务&#xff08;参考文章&#xff09; 1 图片预处理 1 .1 统一图片格式 找到的图片需要首先做相同尺寸的裁剪&#xff0c;归一化&#xff0c;否则会因为图片大小不同报错 RuntimeError: stack expects each tensor to be equal size, but got [3, 667…

【VRTK】【VR开发】【Unity】7-配置交互能力和向量追踪

【前情提要】 目前为止,我们虽然设定了手模型和动画,还能够正确根据输入触发动作,不过还未能与任何物体互动。要互动,需要给手部设定相应的Interactor能力。 【配置Interactor的抓取功能】 在Hierarchy中选中[VRTK_CAMERA_RIGS_SETUP] ➤ Camera Rigs, Tracked Alias ➤ …

VBA技术资料MF85:将工作簿批量另存为PDF文件

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

UE4 基础篇十四:自定义插件

文末有视频地址和git地址 一、概念 虚幻里插件都是用C++写的,C++包括.h文件和.cpp文件,.h头文件通常包含函数类型和函数声明,cpp文件包含这些类型和函数的实现, 你为项目编写的所有代码文件都必须位于模块中,模块就是硬盘里的一个文件夹,包含名为“Build.cs”的C#文件…

BGP笔记实验

IGP(Interior Gateway Protocol)——内部网关协议 OSPF RIP IS-IS IGRP EIGRP EGP(External Gateway Protocol)——外部网关协议 EGP BGP——边界网关协议 AS——自治系统 由单一组织or机构独立维护的网络设备&网络资源的集合 网络范围太大 自治 AS号 为了区分不同…

API网关那些事【架构新知系列】

目前随着云原生ServiceMesh和微服务架构的不断演进&#xff0c;网关领域新产品不断出现&#xff0c;各种网关使用的技术&#xff0c;功能和应用领域也不断扩展&#xff0c;在各有所长的前提下也有很多功能重合&#xff0c;网上各种技术PR文章&#xff0c;评测资料和网关落地实践…

2024年山东省职业院校技能大赛中职组“网络安全”赛项竞赛试题-A

2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-A 一、竞赛时间 总计&#xff1a;360分钟 二、竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 A、B模块 A-1 登录安全加固 180分钟 200分 A-2 本地安全策略设置 A-3 流量完整性保护 A-4 …

Ubuntu18.04安装LeGO-LOAM保姆级教程

系统环境&#xff1a;Ubuntu18.04.6 LTS 1.LeGO-LOAM的安装前要求&#xff1a; 1.1 ROS安装&#xff1a;参考我的另一篇博客Ubuntu18.04安装ROS-melodic保姆级教程_灬杨三岁灬的博客-CSDN博客文章浏览阅读168次。Ubuntu18.04安装ROS-melodic保姆级教程https://blog.csdn.net/…

海外IP代理科普——API代理是什么?怎么用?

随着互联网的不断发展&#xff0c;越来越多的企业开始使用API&#xff08;应用程序接口&#xff09;来实现数据的共享和交流。而在API使用中&#xff0c;海外代理IP也逐渐普及。那么&#xff0c;什么是API代理IP呢&#xff1f;它有什么作用&#xff1f;API接口有何用处&#xf…