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

发布于:2025-09-12 ⋅ 阅读:(22) ⋅ 点赞:(0)

一.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)=>
          <li key={item.id}>{item.name}</li>==>key:React框架内使用,提供列表的更新性能
        )}
      </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状态变量,并将其初始值设置为0
  const [count,setCount]=useState(0);
  // 2.点击事件回调
  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状态变量,并将其初始值设置为0
  const [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)//点击事件中

<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
当把内容嵌套在子组件标签中时,父组件自动在名为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.js,App再父传子下发给Son2)
(难点:父组件获取到子组件Son1的数据后要用变量接收,且要在将来的父传子中传给Son2并动态显示,解决方法是把数据做成状态再下发)

//子传父step3:调用回调函数并传参
const msg1="hello React!!"
function Son1(onGetSonMsg){//子组件1
    return <button onClick={onGetSonMsg(msg1)}>点击</button>
}

//父传子step2:通过props属性接收父组件传值
function Son2(props){
    return <div>接收兄弟组件的数据为:{props.msg}</div>
}

function App(){
    const [msg,setMsg]=useState('')
    //子传父step1:准备回调函数
    const getMsg=msg=>{
        console.log("接收子组件传递数据:",msg)
        setMsg(msg)//状态提升到父组件
    }
    return{
        {*子传父step2:把回调函数作为props值传给子组件*}
        <Son1 onGetSonMsg={getMsg}/>
        {*父传子step1:把接收到msg作为自定义属性传给Son2*}
        <Son2 msg={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.channels
      console.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.自定义一个useXxxx函数,

类似于useState,useEffect,useRef,useContext等React 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;

三.案例:b站评论区


网站公告

今日签到

点亮在社区的每一天
去签到