当前位置: 首页 > news >正文

React18学习笔记(一) 如何创建一个React项目,JSX的基础应用,案例---视频网站评论区

文章目录

      • 一.React开发环境搭建
      • 二.JSX基础
        • 1.什么是JSX?
        • 2.JSX的本质是什么?
        • 3.高频用法
          • 3.1.在JSX中使用表达式
          • 3.2.在JSX中实现列表渲染
          • 3.3.在JSX中实现简单的条件渲染
          • 3.4.在JSX中实现复杂的条件渲染
        • 4.React中的事件绑定
        • 5.React中的组件
        • 6.usestate()函数
          • 6.1.介绍和示例
          • 6.2.状态变量不能直接赋值
        • 7.React组件的样式处理
        • 8.工具库classnames
        • 9.受控表单绑定:使用useState控制表单状态
        • 10.在React中获取DOM元素
        • 11.React中的组件通信
          • 11.1.父传子
          • 11.2.子传父
          • 11.3.兄弟组件通信:状态提升
          • 11.4.跨层级(祖孙)组件通信:Context机制
        • 12.useEffect()函数
          • 12.1.概念和使用
          • 12.2.依赖项参数说明
          • 12.3.清除副作用
        • 13.自定义一个Hook函数和使用规则
          • 13.1.自定义一个useXxxx函数,
          • 13.2.
      • 三.案例:b站评论区
        • 1.渲染评论功能
        • 2.删除评论
        • 3.渲染Tab和点击高亮
        • 4.排序功能
        • 5.添加评论
        • 6.id处理和时间处理
        • 7.提交新评论后,输入框清空内容并重新聚焦
        • 8.优化
          • 8.1.使用useEffect获取数据
          • 8.2.自定义hook封装请求逻辑
          • 8.3.封装评论组件CommentItem
          • 8.4.知识点:为什么要把渲染评论数据的部分单独做成一个子组件?
            • 8.4.1. 功能型组件(容器组件)
            • 8.4.2. UI型组件(展示组件)

一.React开发环境搭建

如何创建一个react项目?

npx create-react-app react-demo
cd react-demo(这是项目名称)
npm start

二.JSX基础

1.什么是JSX?

它是JavaScript和XML的缩写,表示在JS代码中编写HTML模板结构
这是React中编写UI模板的形式 :

//App.js
const message="this is message"
function App(){return{<div><h1>this is title</h1>{{message}}</div>}
}

-为啥这么设计?
-既想要HTML声明式模板的写法,又想要JS的可编程能力

2.JSX的本质是什么?

JSX不是标准的JS语法,而是JS的语法拓展,浏览器本身不能识别,要通过解析工具(babel)解析之后,才能在浏览器中运行

解析之前:<div>this is div</div>
解析之后:import {jsx as _jsx} from 'react/jsx-tuntime'/*#__*/_jsx("div",{children:"this is div"});
3.高频用法
3.1.在JSX中使用表达式

在JSX中可以通过大括号{}识别JS表达式,如变量,函数或方法的调用

//App.js
const count=100
function getName(){return "zhangsan"}
function App(){return{<div><h1>this is title</h1>1.使用引号传递字符串{'this is message'}2.识别JS变量{count}3.函数调用和方法调用{getName()}{new Date(.getDate())}4.使用JS对象:一般在控制内联样式时用到<div style={{color:'red'}}>hello</div></div>}
}
export default App
3.2.在JSX中实现列表渲染

使用map方法遍历渲染列表

//列表渲染
const  list =[{id:1,name:"John"},{id:2,name:"Jane"},{id:3,name:"Doe"}
]
function App() {return (<div><ul>      {list.map((item)=>{*key:React框架内使用,提供列表的更新性能*}<li key={item.id}>{item.name}</li>)}</ul></div>);
}
3.3.在JSX中实现简单的条件渲染

使用三元表达式实现基础的条件渲染

// 条件渲染
const loading=true;
function App() {return (<div>{/* 三元表达式 */}{loading? <h1>Loading...</h1>:null}</div>);
}
export default App;
3.4.在JSX中实现复杂的条件渲染

自定义函数+if判断

const article=1;
function getArticle(article){if(article===0) return <div>我是无图文章</div>;if(article===1) return <div>我是单图文章</div>;return <div>我是三图文章</div>;
}
function App() {return (<div>{/* 复杂条件渲染 */}{getArticle(article)}</div>);
}
export default App;
4.React中的事件绑定

语法:on+事件名={事件处理程序}//驼峰命名法
示例:

function App() {// 事件处理程序const handleClick=()=>{console.log("按钮被点击了");}return (<div><button onClick={handleClick}>点击</button></div>);
}
5.React中的组件

组件是用户界面的一部分,有自己的逻辑和外观,组件之间可互相嵌套,可多次复用
在React中,一个组件是一个首字母大写的函数,内部存放组件的逻辑和视图UI,渲染组件只需把组件当成标签来写即可

// 定义一个MyButton组件
function MyButton(){return <button>自定义按钮</button>;
}function App() {return (<div>{/* 使用组件:两种方式 */}<MyButton/><MyButton></MyButton></div>);
}
export default App;

也可以把子组件提取为单独文件如下:

// src/components/MyButton.js
import React from 'react';
function MyButton() { return <button>自定义按钮</button>;}
export default MyButton; // 默认导出组件//App,js
import React from 'react';
import MyButton from './components/MyButton'; // 导入组件
function App() {return (<div><h1>主应用</h1><MyButton onClick={() => alert('Clicked!')}>点击按钮</MyButton></div>);
}
export default App;
6.usestate()函数
6.1.介绍和示例

useState是一个React Hook函数,允许用户向组件添加一个状态变量,从而控制影响组件的渲染结果
*状态变量和普通变量的不同之处:一旦发生变化,组件的视图UI也会跟着变化(数据驱动视图)

/*useState是一个函数,返回值是一个数组
数组中的第一个参数是状态变量,第二个参数是set函数,用来修改状态变量示例:useState的参数作为count的初始值
*/
import { useState } from "react";
function App() {// step1:调用useState()初始化count状态变量,并将其初始值设置为0const [count,setCount]=useState(0);// step2:点击事件回调const handleClick=()=>{setCount(count+1)//效果:除了用新值取代旧count值外,还会重新渲染组件}return (<div><h1>Hello, React!</h1><button onClick={handleClick}>点击我</button>{count}</div>);
}
export default App;
6.2.状态变量不能直接赋值

在React中,状态被认为是只读的,只能替换它而非修改它,直接修改状态不能引发视图更新
上例中,改成如下,视图将不会更新:

//错误做法:const handleClick=()=>{//setCount(count+1)//除了修改count,还要重新渲染组件count++//视图不会被更新console.log(count)}//正确做法:const handleClick=()=>{setCount(count+1)//内部:使用新值count+1 ,替换旧值}

对象类型的状态变量同样遵守状态不可变的规则,应该始终传给set方法一个全新的对象来进行修改


import { useState } from "react";
function App() {// step1:调用useState()初始化count状态变量,并将其初始值设置为0const [form,setForm]=useState({name:'张三'});// 2.点击事件回调const handleClick=()=>{// form.name='李四'//不能直接修改setForm({...form,name:'李四' // 更新name字段})
}    return (<div><h1>Hello, React!</h1><button onClick={handleClick}>点击我</button>{form.name}</div>);
}
export default App;
7.React组件的样式处理

React有两种方式来控制样式

1.行内样式:
<h1 style={{color:'gold',fontSize:'25px'}}>Hello, React!</h1>2.class类名控制:
//index.css
.active{color:red
}
//App.js
import './index.css'
<div className="active">hello</div>
8.工具库classnames

一个简单的JS库,通过条件动态控制class类名的显示,用于优化类名控制
语法:

<div className={classNames('nav-item',{active:type==='item-type'})}>hello</div>
其中,静态类名:nav-item动态类名==>对象结构key表示要控制的类名(active),value表示条件(type==='item.type'),该条件是true的时候显示类名即:
没有点击时它叫:
<div class='nav-item'>hello</div>
点击高亮时它叫:
<div class='nav-item active'>hello</div>
然后通过类名来控制相关样式

安装:npm i classnames
使用:

import classnames from "classnames"
<div className={classnames('nav-item',{active:true})}>hello</div>
9.受控表单绑定:使用useState控制表单状态

React中有两种状态:

  • react的状态:state,通过useState生成的状态值,通过它可以生成状态,这种状态值的特点是其变化会引发视图的更新,其值不能直接改变,需要在setState中使用提供新值覆盖旧值
  • 表单自身的状态:value,通过e.target.value可以拿到用户输入的文本内容
    在这里插入图片描述

-如何实现受控表单绑定?
-state绑定到input的value属性,然后在input中把最新的value值设置给state

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

示例:后续视频网站评论区的小案例中添加新评论功能时使用到

10.在React中获取DOM元素

(不建议直接操作DOM,vue和react的本意就是通过减少DOM的操作来提高使用体验)
使用到了钩子函数useRef
步骤:

//引入
import useRef from "React"
//1.使用useRef创建ref对象,并与JSX绑定
const inputRef=useRef(null)
//2.通过inputRef.current拿到DOM对象
console.log(inputRef.current)//一般在事件内使用inputRef.current获取DOM<input ref={inputRef} />
11.React中的组件通信
11.1.父传子

步骤:(同Vue)父组件在子组件占位符上绑定自定义属性,子组件通过props参数接收传值
示例:(React中的子组件直接写成函数形式)

function Son(props){return <div>我是子组件,接收到的数据是:{props.info}</div>
}
function App(){return {<div><Son info={message666}></Son></div>}
}

其中,props属性:1.可传递任意数据;2.是只读对象不能修改,数据只能在父组件修改

特殊的props----children
当把内容嵌套在子组件标签中时(<div>传给子组件</div>),父组件自动在名为children的props属性中接收该内容

function Son(props){console.log(props)//children:<div>传给子组件</div>
}
function App(){return {<div><Son><div>传给子组件</div></Son></div>}
}
11.2.子传父

步骤:

  • step1:父组件定义一个回调函数,用于接收子组件传过来的数据
  • step2:父组件将此回调函数作为props传给子组件
  • step3:子组件在需要的时候(如:点击事件中)调用这个传递过来的函数并传参
function Son({onGetSonMsg}){const msg1='HELLO"return {//step3:子组件在点击事件中调用回调函数,并将数据作为参数传入<button onClick={()=>onGetSonMsg(msg1)}>点击</button>}
}
function App(){//step1:父组件准备回调函数getMsg,用于接收子组件的数据const getMsg=(msg)={console.log("接收子组件的数据为:",msg)}return {<div>//step2: 父组件将这个回调函数作为props传递给子组件<Son onGetSonMsg={getMsg}></Son></div>}
}
11.3.兄弟组件通信:状态提升

        (状态提升:我的理解是都把状态提升到共同的父组件App.js中,类似于vue中的EventBus或vuex)
思路:
        将共享状态提升到最近的共同父组件中,然后通过props传递给子组件,同时将修改状态的函数也传递给子组件,这样兄弟组件就可以通过父组件来同步状态
(流程:Son1子传父给父组件App,父组件App再父传子下发给Son2)
(难点:父组件获取到子组件Son1的数据后要用变量接收,且要在将来的父传子中传给Son2并动态显示,解决方法是把数据做成状态再下发)

//第三步--子传父step3:在点击事件中调用回调函数并传参
const msg1="hello React!!"
function Son1(onGetSonMsg){//子组件1return <button onClick={onGetSonMsg(msg1)}>点击</button>
}//第六步--父传子step2:通过props属性接收父组件传值
function Son2(props){return <div>接收兄弟组件的数据为:{props.msgToSon2}</div>
}function App(){const [msg,setMsg]=useState('')//第一步--子传父step1:准备回调函数const getMsg=message=>{//console.log("接收子组件传递数据:",message)setMsg(message)//第四步--状态提升到父组件:把子传父获取到的数据作为状态变量msg的最新值}return{{*第二步-子传父step2:把回调函数作为props值传给子组件*}<Son1 onGetSonMsg={getMsg}/>{*第五步--父传子step1:把状态变量msg作为自定义属性的值传给Son2*}<Son2 msgToSon2={msg}/>}
}
11.4.跨层级(祖孙)组件通信:Context机制
  • 使用createContext方法创建一个上下文对象Ctx
  • 在顶层组件(App)中通过Ctx.Provider为组件提供数据
  • 在底层组件(Grandson)中通过钩子函数useContext获取数据
//App.js
import { React,useState,  createContext, useContext } from 'react';
const msg='hello'//准备App==>Grandson的数据
// step1:使用createContext方法创建一个上下文对象Ctx
const Ctx=createContext("默认消息")
return ({/* step2:在顶层组件(App)中通过Ctx.Provider为组件提供数据 */}<Ctx.Provider value={msg}><div style={{padding: '20px', border: '1px solid #ccc'}}><h2>App组件(数据提供方)</h2><Son /></div></Ctx.Provider>
)// 中间组件
function Son(){return (<div style={{margin: '15px', padding: '15px', border: '1px dashed #999'}}><h3>Son组件(中间层)</h3><Grandson /></div>)
}// 底层组件
function Grandson(){// step3:在底层组件(Grandson)中通过钩子函数useContext获取数据const message=useContext(Ctx)console.log(message)return (<div style={{margin: '10px', padding: '10px', background: '#f0f0f0'}}><h4>Grandson组件(数据消费方)</h4><p>接收到的消息:<strong>{message}</strong></p></div>)
}

在这里插入图片描述

12.useEffect()函数
12.1.概念和使用

useEffectReact Hook函数,用于在React组件中创建不是由事件引起的,而是由渲染本身引起的操作(如:发送AJAX请求,更改DOM等)
常见场景:页面渲染完成,组件没有发生任何用户事件,需要向服务器要数据,这个过程就是"仅由渲染引起的操作"
语法:useEffect(()=>{},[])
其中,
{}被称为副作用函数,在函数内写要执行的操作
[]是可选数组,放置依赖项,不同依赖项影响副作用函数的执行
示例:

import {useEffect,useState} from "react"
const URL='https://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()const list=jsonRes.data.channelsconsole.log(list)//数组元素:{id: 0, name: '推荐'}setList(list)}getList()},[])return(<div><ul>{list.map(item=><li key={item.id}>{item.name}</li>)}</ul></div>)
}
export default App
12.2.依赖项参数说明

根据传入依赖项的不同,会有不同的执行表现:

  • 1.空数组依赖:只在初始渲染时执行一次(见上例)
  • 2.没有依赖项:组件初始渲染+组件更新时执行
useEffect(()=>{console.log("副作用函数执行了.....")
})
  • 3.添加特性依赖项:组件初始渲染+特性依赖变化时执行
const [count,setCount]=useState(1001)
useEffect(()=>{console.log("副作用函数执行了.....")
},[count])/count的值一变化就打印输出语句
12.3.清除副作用

副作用操作:在useEffect中编写的有渲染本身引起的对接组件外部的操作
(如:在useEffect中开启了一个定时器,我们想在组件卸载时清理定时器,这个过程称为清理副作用)

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

示例:(点击卸载了子组件,但定时器还在运行)

import React, { useState , useEffect } from 'react';function Son() => {useEffect(() => {// 创建定时器const timer = setInterval(() => {console.log("定时器运行中...");// 这里执行定时任务}, 1000);// 关键:返回清理函数----清除副作用return () => {clearInterval(timer); // 组件卸载时清除定时器console.log("定时器已清除");};}, []); // 空依赖数组表示只在挂载/卸载时执行return <div>子组件内容</div>;
};function App() {const [show, setShow] = useState(true);return (<div><button onClick={() => setShow(!show)}>{show ? "隐藏组件" : "显示组件"}</button>{show && <Son />}  {/* 条件渲染 */}</div>);
}export default App;
13.自定义一个Hook函数和使用规则
13.1.自定义一个useXxxx函数,

类似于useState,useEffect,useRef,useContextReact Hook函数,以实现逻辑的封装和复用
需求:点击按钮控制div的显示和隐藏

import React, { useState  } from 'react';
function App() {const [show, setShow] = useState(true);const isToggle=()=>{setShow(!show)}return (<div>{show&&<div>hello react!!</div>}<button onClick={isToggle}>{show ? "点击隐藏" : "点击显示"}</button></div>);
}
export default App;

封装一个可复用的逻辑useToggle如下:

import React, { useState  } from 'react';function useToggle(){const [show, setShow] = useState(true);const isToggle=()=>{setShow(!show)}return{//对外暴露状态和函数让其他组件使用show,isToggle}
}function App() {const {show,isToggle}=useToggle()return (<div>{show&&<div>hello react!!</div>}<button onClick={isToggle}>{show ? "点击隐藏" : "点击显示"}</button></div>);
}
export default App;
13.2.
  • 只能在组件中或其他自定义Hook函数中调用
  • 只能在组件的顶层调用,不能嵌套在if,for或其他函数中

三.案例:b站评论区

最终效果:
在这里插入图片描述
要求:

  • 渲染评论列表
  • 删除评论功能
  • 渲染Tab导航栏并实现点击高亮
  • 评论列表排序功能
  • 添加评论
  • 时间格式的优化
1.渲染评论功能

思路:1.使用useState维护评论列表;2.使用map遍历列表数据
(代码仅做过程参考,后续有完整代码)

//App.js//step1:使用useState维护list
import { useState } from "react";
const list=[/*模拟列表数据*/]
function App() {const [commentList,setCommentList]=useState(list);return (<div>{*评论列表*}{commentList.map(item=>(<div key={item.rpid} className='reply-item'>...<imgclassName="bili-avatar-img"alt='':src={item.user.avatar}/>...{*用户名*}{item.user.uname}{*评论时间*}{item.ctime}{*评论数量*}点赞量:{item.like}</div>))}</div>);
}
2.删除评论

要求:

  • 只有自己(模拟用户)发的评论才显示删除效果
  • 点击删除:删除当前评论,列表中不再显示

思路:1.条件渲染:删除按钮的显示;2.点击删除:拿到当前点击id,在评论列表中过滤filter

//评论列表数据:
const list=[{rpid:3,user:{uid:'123456',avatar:'xxx'uname:'张三'},comment:"哎呦不错哦",ctime:'9-9 09:59',like:666},
{rpid:3,user:{uid:'123456',avatar:'xxx'uname:'张三'},comment:"哎呦不错哦",ctime:'9-9 09:59',like:666
},.....   
]//(模拟)当前登录用户的信息
const user={uid:'123456',avatar,uname:'张三'
}function App(){const [commentList,setCommentList]=useState(0);const handleDel=(id){//过滤:setCommentList(commentList.filter(item=>item.rpid!=id))}
return(
{*判断:*}
{user.uid===list.user.uid&&<spanonClick={()=>handleDel(item.rpid)} className="delete-btn">删除</span>}总结:绑定删除事件handleDel,删除的逻辑是调用setCommentList,更新commentList状态为过滤掉点击id对应的用户数据
3.渲染Tab和点击高亮

思路:

  • 点击谁,就把谁的type记录下来
  • 和遍历的每一项type做匹配
  • 谁匹配到就新增负责高亮的类名(.active)
//tab导航数组
const tab={{type:'hot',text:'最热'}time      最新
}const App=()=>{
//tab切换:
1/点击谁,谁就高亮
const [type,setType]=useState('hot')
const handleTabChange=(type)=>{setType(type)
}return(
<li className="nav-sort">{tab.map(item=><span key={item.type} onClick={()=>handleTabChange(item.type)}className={`nav-item ${type===item.type&&'active'}`>{item.text}</span>)}//span nav-item 最新
</li>
总结:1.动态渲染tab,替换"最新"{item.text},设置key2.绑定点击事件,事件中调用setType,更改状态为当前用户点击的是最新或最热3.设置高亮:动态设置类名,className={`nav-item ${type===item.type&&'active'}`}
4.排序功能

需求:点击"最新"和"最热"分别按时间倒序排列和按点赞数排列
思路:

  • 把评论列表的状态数据进行不同的排列处理,当成新值传给set函数,重新渲染UI视图

小工具:

    lodash函数库>官网>orderBy安装:npm i lodash -d引入:import lodash from "./lodash"

代码:

const [commentList,setCommentList]=useState(_.orderBy(list,'like','desc'))
const handleTabChange=(type)=>{setType(type)//基于列表的排序if(item.type==='hot'){// 最热const newList=lodash.orderBy(commentList,['likes'],['desc'])setCommentList(newList)}else{// 最新const newList=lodash.orderBy(commentList,['ctime'],['desc'])setCommentList(newList)}
}
总结:,直接套语法
5.添加评论

使用到了value和state两种状态的联动(见知识点:9.受控表单绑定:使用useState控制表单状态)

  const [inputValue,setInputValue]=useState('')const handleSubmit=()=>{console.log('发布评论')// 判断非空if(inputValue.trim()==='') return;// 发布评论const myComment={rpid:`c${commentList.length+1}`,user:{...currentUser},content:inputValue,ctime:new Date().toLocaleString('zh-CN').split(' ')[0].replace(/\//g, '-'),likes:0}// 更新状态// 1.更新评论列表setCommentList([...commentList,myComment])// 2.清空input输入框setInputValue('')//后续知识点:受控表单绑定}************************************* {/* 评论输入区 */}<div className="comment-input-area"><inputtype="text"placeholder="发个友善的评论吧..."value={inputValue}onChange={e=>setInputValue(e.target.value)}className="comment-input"/><button className='submit-btn' onClick={handleSubmit}>发布</button></div>
6.id处理和时间处理
  • 1.rpid要求一个唯一的随机数id–uuid
uuid官网>Quickstart>- 复制安装命令: npm install uuid- 引入:import {v4 as uuidV4} from "uuid"//v4是里面有的,uuidV4是自定义后续使用的- 使用:见下方代码
  • 2.ctime要求以当前时间为标准,生成固定格式–dayjs库
dayjs官网(轻量化,专门做时间处理的插件)- 安装:npm install dayjs- 引入:import dayjs from 'dayjs'- 使用:见下方代码

最终代码:

    // 发布评论const myComment={rpid:uuidV4(),//当成一个可执行方法来使用,效果:生成独一无二的iduser:{...currentUser},content:inputValue,ctime:dayjs(new Date()).format('MM-DD hh:mm'),likes:0}// 更新状态// 1.更新评论列表setCommentList([...commentList,myComment])// 2.清空input输入框setInputValue('')}
7.提交新评论后,输入框清空内容并重新聚焦

思路:
- 清空内容:把控制input的value状态设置为空
- 重新聚焦:获取input的DOM元素,调用其focus方法
代码:

const [inputValue,setInputValue]=useState('')
const inputRef=useRef(null)const handleSubmit=()=>{console.log('发布评论')// 判断非空if(inputValue.trim()==='') return;// 发布评论const myComment={rpid:uuidV4(),//当成一个可执行方法来使用,效果:生成独一无二的iduser:{...currentUser},content:inputValue,ctime:dayjs(new Date()).format('MM-DD hh:mm'),likes:0}// 更新状态// 1.更新评论列表setCommentList([...commentList,myComment])// 2.清空input输入框setInputValue('')//3.获取DOM元素inputRef.current.focus()}*************************************{/* 评论输入区 */}<div className="comment-input-area"><inputtype="text"placeholder="发个友善的评论吧..."value={inputValue}onChange={e=>setInputValue(e.target.value)}className="comment-input"ref={inputRef}/><button className='submit-btn' onClick={handleSubmit}>发布</button></div>
8.优化
  • 1.使用useEffect获取数据—使用请求接口的方式获取评论列表并渲染(之前是写死的静态数据)
  • 2.自定义hook封装请求逻辑—使用自定义Hook函数封装数据请求的逻辑
  • 3.评论Item组件封装—把评论的每一项抽象成一个独立的组件实现渲染
8.1.使用useEffect获取数据

步骤:
- step1:使用json-server工具模拟接口服务,通过axios发送接口请求

安装:npm i json-server -D
根目录创建db.json(模拟后端数据库),把App.js中的const list=[]复制过去,格式改成:{list:[{},{},{}]}package.json中新增自定义配置命令"script"{"serve":"json-server db.json --port 3004"}
(先npm start启动react项目)启动:不用json-server --watch db.json,直接用npm run serve安装:npm i axios 

-step2:使用useEffect调用接口获取数据

function App(){const [commentList,setCommentList]=useState([])useEffect(()=>{// 请求数据async function getList(){const res=await axios.get('http://localhost:3004/list')setCommentList(res.data)}getList()},[])
}
8.2.自定义hook封装请求逻辑

封装步骤:
编写useXxxx函数>函数内部放封装的逻辑>把对外暴露的状态和方法return出去>组件解构赋值调用状态和方法

// 优化二:自定义hook,封装调用数据的函数
function useGetList(){// 优化一:注销静态数据list,使用接口调用后台数据const [commentList,setCommentList]=useState([])// 使用useEffect调用接口获取数据useEffect(()=>{//请求数据async function getList(){const res=await axios.get('http://localhost:3004/list')console.log(res.data)setCommentList(res.data)}getList()},[])return {setCommentList,commentList}
}function App(){/*const [commentList,setCommentList]=useState([])useEffect(()=>{// 请求数据async function getList(){const res=await axios.get('http://localhost:3004/list')setCommentList(res.data)}getList()},[])*/const {commentList,setCommentList}=useGetList()
}
8.3.封装评论组件CommentItem
  • 把代码剪切到CommentItemreturn(/*放HTML就用圆括号*/)中,原位置<CommentItem />
  • 父传子:把item数据通过自定义属性传过去
  • 子传父:处理里面原先的的删除按钮
// 优化三:把每条评论都抽离成单独的UI组件-CommentItem
function CommentItem({item,onDel}){return (<div key={item.rpid} className="item-item"><imgsrc={item.user.avatar}alt={item.user.uname}className="avatar"/><div className="content"><div className="header"><span className="username">{item.user.uname}</span><span className="time">{item.ctime}</span></div><div className="text">{item.content}</div><div className="actions"><span className='like'>{item.likes}</span>{/* 判断:如果是当前用户,则显示删除按钮 */}{item.user.uid === currentUser.uid && (<button onClick={()=>onDel(item.rpid)}>删除</button>)}</div></div></div>)
}function App(){.....return({/* 评论列表 */}<div className="comment-list">{commentList.map(item => (<CommentItem key={item.id} item={item} onDel={handleDeleteComment}/>))}</div>)
}
8.4.知识点:为什么要把渲染评论数据的部分单独做成一个子组件?

简答:让容器组件App专注于业务逻辑,把样式渲染的部分都提取为专门的UI组件,这是React组件的分工特点

在React中把组件划分为功能型组件(容器组件)和UI型组件(展示组件)

8.4.1. 功能型组件(容器组件)

定位:承担业务逻辑、状态管理、数据获取等核心功能

特点:

通常作为顶层组件(如App组件)
包含状态(state)和生命周期方法
处理数据交互和业务规则
向UI组件传递数据和回调函数
8.4.2. UI型组件(展示组件)

定位:专注UI呈现和交互样式,保持"纯净"的渲染逻辑

特点:

无状态(可使用React.memo优化)
通过props接收数据和回调
不处理业务逻辑或数据获取
高度可复用和解耦

文章转载自:

http://HJ6MV9zI.ykmtz.cn
http://qe83pClb.ykmtz.cn
http://4kgNlbi6.ykmtz.cn
http://eFYyPMyE.ykmtz.cn
http://x7dZYLYP.ykmtz.cn
http://gpjw2mHr.ykmtz.cn
http://Gtso8SaT.ykmtz.cn
http://qak0MdlL.ykmtz.cn
http://lnMFWLqN.ykmtz.cn
http://LMY42jhn.ykmtz.cn
http://OZmLTKGf.ykmtz.cn
http://4YuU49lB.ykmtz.cn
http://dqeURq5B.ykmtz.cn
http://G1RQscdP.ykmtz.cn
http://y0crXGpN.ykmtz.cn
http://TstgNSCA.ykmtz.cn
http://v6rHJh29.ykmtz.cn
http://gkbeilAg.ykmtz.cn
http://4aO1Y6Uw.ykmtz.cn
http://yyJBtUQK.ykmtz.cn
http://cfFxOjTk.ykmtz.cn
http://ouTuzkij.ykmtz.cn
http://SIr879gO.ykmtz.cn
http://JjETbqW2.ykmtz.cn
http://6igHJDxo.ykmtz.cn
http://L0DChZom.ykmtz.cn
http://kGHH5v5G.ykmtz.cn
http://m47xC5Gh.ykmtz.cn
http://d0psXwxa.ykmtz.cn
http://wjzbZxVo.ykmtz.cn
http://www.dtcms.com/a/379614.html

相关文章:

  • 【Threejs】学习笔记
  • 图像显示技术与色彩转换:从基础原理到实际应用
  • C 语言实现 I.MX6ULL 点灯(续上一篇)、SDK、deep及bsp工程管理
  • 飞桨paddlepaddle旧版本2.4.2安装
  • 2.5 DNS(Domain Name System)
  • CK: 03靶场渗透
  • User类CRUD实现
  • AFSim2.9.0学习笔记 —— 4.2、ArkSIM文件结构介绍及项目结构整理
  • JavaScript WebAPI 指南
  • 计算机毕业设计 基于Hadoop的南昌房价数据分析系统的设计与实现 Python 大数据毕业设计 Hadoop毕业设计选题【附源码+文档报告+安装调试】
  • 电路学习(六)三极管
  • 照度传感器考虑笔记
  • 在springboot中使用okhttp3
  • Android开发之Android官方模拟器启动失败问题跟踪排查
  • 第4节-排序和限制-FETCH
  • 2025.9.11总结
  • 三大范式是什么?
  • 传统文化与现代科技的完美融合:文昌帝君灵签系统开发实践
  • 避坑指南:从原理出发解决常见问题
  • 什么是特征冗余度?
  • 数据结构----栈的顺序存储(顺序栈)
  • Java 线上问题排查:从基础到高级的全面指南
  • 浅谈Nacos配置中心
  • 美国CISA发布通用漏洞披露 (CVE) 计划愿景
  • 软考中级习题与解答——第五章_面向对象方法(1)
  • 硬件驱动——I.MX6ULL裸机启动(2)
  • Linux 进程深度解析(6):资源隔离的底层实现 (Namespace、Cgroups 与容器化)
  • 【AI大模型面试宝典60题】1-5
  • AUTOSAR Adaptive Platform 日志与追踪 (Log and Trace) 规范深度解析
  • Claude Code + 自定义模型体验