一天搞定React(3)——Hoots组件【已完结】

news2024/11/24 19:01:58

    Hello!大家好,今天带来的是React前端JS库的学习,课程来自黑马的往期课程,具体连接地址我也没有找到,大家可以广搜巡查一下,但是总体来说,这套课程教学质量非常高,每个知识点都有一个小案例,最后有一个总的美团外卖案例教学,大家可以看看我这篇文章,如果能够帮到你们,还请多多点赞o( ̄▽ ̄)d支持支持🌹,如果文章中有错误的或者是遗漏信息,可以在评论区指出或者是与我私信。我看到了消息,一定会及时更正过来∠(°ゝ°)。话不多说,直接开学💪⛽️!

     本篇教学已完结,具体可查看教程:
1. 一天搞定React(1)——React安装与配置
2. 一天搞定React(2)——JSX语法
3. 一天搞定React(3)——Hoots组件
4. 一天搞定React(4)——Redux
5. 一天搞定Recat(5)——ReactRouter(上)
6. 一天搞定React(5)——ReactRouter(下)

文章目录

  • 组件学习
    • 创建组件
    • 组件的状态
      • 修改状态
        • 【案例】B站评论
        • classNames优化
      • 状态操作表单元素
    • useRef
  • 组件通讯
    • 父子组件通讯
      • 父传子通讯
      • 简化props
    • 子到父通讯
    • 非父子通讯
      • 兄弟关系
      • 后代关系
  • useEffet的使用
    • useEffect扩展
    • useEffect应用——发送请求
      • Json-server
      • 使用axios
  • Hooks

组件学习

创建组件

src目录下新建一个App.js文件,这个App.js就是我们创建的新的组件。

其实组件就是一个函数,所以我们只需要创建函数就能完成组件的创建。

在App.js文件中编写以下内容:

  1. 创建一个函数

    const 组件名 =()=>{
    	return(
      	<div>
        	App组件
        </div>
      )
    }
    
  2. 导出组件

    export default 组件名
    
  3. 回到index.js文件,导入App组件文件路径

    import 组件名 from '文件路径'
    
  4. index.js文件中使用组件

    root.render(
      <div>
        <组件库 />
      </div>
    );
    

组件的状态

状态(state):是可以让页面内容发生变化的数据。

使用方法:useState() 函数,如果需要修改状态就调用setCount(状态最新值)函数。

image-20240719161152619

const [count, setCount] = useState(默认值)

其中第一个元素是当前状态的值,第二个元素是更新该状态的函数。

我们来写一个计数器来更好得到理解state:

import { uesState } from 'react'

const App =()=>{
    const [count, setCount] = uesState(10)
    return(
        <div>
            <h1>计数器:{count} </h1>
            <button onClick={()=> setCount(count+1)}>+1</button>
            <button onClick={()=> setCount(count-1)}>-1</button>
        </div>
    )
}
export default App

修改状态

规则:不要直接修改当前状态的值,应该创建新值。

注意:如果没有遵循该规则,会导致报错或组件无法重新渲染

如图:image-20240719165054282

在数组类型也是符合这种规则,如图:

image-20240719165151883

扩展:...list这个是表示之前的元素

setList([...list,‘新的内容'])
【案例】B站评论

我们要实现下图👇案例

image-20240719170323295

具体内容要求是有:

  1. 使用组件搭建结构
  2. 导航Tab的渲染和操作
  3. 评论列表的渲染和操作

安装Sass包

在这个项目当中我们需要使用到Sass更好的绘制样式形状,所以会用到Sass。如果你还不会Sass的话,可以跟我这之前的写过的一篇文章进行学习,相信你很快就能上手并熟练运用的💪⛽️

文章:一天搞定前端必备开发技能 (2)——less和Sass预处理器的使用_项目里嵌入 less 预处理器-CSDN博客

安装命令:

npm i sass

之后再src文件夹中创建一个app.scss文件用来编写sass样式。

之后我们在App.js文件导入scss文件和图片

导入图片

使用本地图片,需要手动导入。

❌不能像html一样直接在src导入图片路径

import 图片名称 from "图片路径"

const App =()=>{
 return(
     //使用图片
     <img src={"图片名称"} alt="图片备注"></img>
 )
}
export default App

在这里我准备好了模版结构和样式给大家,大家直接复制粘贴到App.js文件当中即可;

🔗链接:B站评论案例笔记.md

注意⚠️:图片没有提供给大家,在代码中如果涉及到图片的请自行修改🌺。

组件结构搭建完成之后,我们就可以开始编写交互逻辑啦。

一共要实现以下👇三个功能:

  1. 根据状态渲染评论列表
//评论列表数据
const defaultList = [
    {
      //评论id
      rpid: 3,
      //用户信息
      user: {
        uid: "13258165",
        avatar: "https://未知信息",
        uname: "周杰伦",
      },
      //评论内容
      content: "哎哟,不错哦",
      //评论时间
      ctime: "10-1808:15",
      //喜欢数量
      like: 98,
      //0:未表态 1:喜欢 2:不喜欢
      action: 0,
    },
  ];

使用useState来修改状态

const App=()=>{
	const[list,setList] = useState(defaultList)
}

列表渲染

<div classNaem="reply-list">
	{/*评论区*/}
  {
    list.map(item=>{
      return 列表项目
    })
  }
</div>
⚠️不要忘记了给每一个列表项添加key属性

image-20240719181606487

然后将这些评论编程动态的:

  • 评论头像image-20240719181651526

  • 评论名称image-20240719181711687

  • 评论事件image-20240719181751290

  • 喜欢数量image-20240719181822720

然后评论列表的渲染就基本完成了

  1. 删除评论

删除按钮的特性就是只有自己发表的评论才能够删除的,那么如何来实现呢?那就是使咱们登录用户的ID来索引判断是否是自己发表的评论。

{user.uid === item.user.uid &&() <span className="delete-bt">删除</span>)}

创建删除评论函数

//先给删除按钮绑定一个点击事件
onClick={onDelete(item.rpid))

//删除评论
const onDelete=rpid=>{
	console.log(rpid)
}

最后用数组的过滤方法删除掉渲染

//删除评论
const onDelete=rpid=>{
	setList(list.filter(item=> item.rpid !== rpid))
	//如果要删除数组中的元素,需要调用 filter方法,并且一定要调用setList
}
  1. 喜欢和不喜欢

通过传入的action来判断是否是喜欢还是不喜欢。

image-20240719183954662

绑定点击事件

onClick={() =>onLike(item.rpid)}

//创建一个喜欢的函数
const onLike=rpid=>{
	console.log(rpid)
}

高亮的效果显示其实就是一个取反的操作;喜欢的数量改变,还是一样先拿到action,如果是喜欢的话,就让item.like进行加减。

image-20240719184351543

不喜欢渲染也是一样的,这里我就不过多赘述啦!

image-20240719184457959

classNames优化

项目开发中,当多个类名都是动态的,手动处理会变得非常困难。

如果有多个类别都是动态的,那这个时候再来进行手动处理呢,会变得非常的困难,比如咱们来看一个例子,这边是我们经常用的一种类别处理的方式。但如果再有一个。也是动态的,你可能需要。再写一个三元表达式。3个第4个呢,再手动。那有没有更好的方式呢?

<button className={disabled ?'btnbtn-disabled':'btn'}></button>

今天我们就来学习使用classnames包来优化类名处理。

  1. 安装包

    npm i classnames
    
  2. 导包

    import classNames from 'classnames'
    

使用方法:

咱们直接上案例来进行学习

import classNames from 'classnames'
const App =()=>{
  // 是否禁用
  const [disabled,setDisabled] = useState(false)
  // 大小
  const [size,setSize] =useState('small')

    return(
        <div>
          <button>按钮</button>
          <hr/>
          <div>
            操作:
            <button onClick={()=> setDisabled(true)}>禁用</button>
            <button onClick={()=> setSize('small')}>变小</button>
            <button onClick={()=> setSize('large')}>变大</button>
          </div>
        </div>
    )
}
export default App

以上👆就是我们会对这个按钮,给它来去加一些相应的样式,那并且这边,我还准备了两个状态,一个表示是否禁用一个。是按钮的大小,然后接下来呢,咱们还有分别有3个按钮进行控制。

  1. 如果类名一直都有的,就在函数参数内写好

    <button className={classNames('一直都用的class')}>按钮</button>
    
  2. 使用逻辑运算(适合处理单个类名)

    //语法模版
    <button className={className('一直都用的class',条件1 && ‘样式1’)}>按钮</button>
    
    //联系案例
    // 还需要联系上面的useState状态来判断
     const [disabled,setDisabled] = useState(false)
    
    <button className={classNames('btn',disabled && 'btn-disabled')}>按钮</button>
    

    这里classNames函数会根据disabled的值来决定是否添加'btn-disabled'这个类名。如果disabledtrueclassName将会是'btn btn-disabled';如果disabledfalseclassName将会是'btn'

  3. 使用对象语法(适合处理多个类名)

    //语法模版
    <button className={className('一直都用的class',{
    	'样式1':条件1,
      '样式2':条件2,
      '样式3':条件3,
    })}>按钮</button>
    
    //联系案例
    
      // 是否禁用
      const [disabled,setDisabled] = useState(false)
      // 大小
      const [size,setSize] =useState('small')
      const [disabled,setDisabled] = useState(false)
    
    <button className={className('btn',{
     
    })}>按钮</button>
    

    注意⚠️:

    useState(false) 表示初始状态 disabled 的值被设置为 falsesetDisabled 是一个函数,你可以在组件中调用它来更新 disabled 的状态。所以,初始时 disabled 的取值是 false

完整代码:

import {useState} from 'react'
import classNames from 'classnames'
// import 'App.css'; // 导入CSS模块
const App =()=>{
  // 是否禁用
  const [disabled,setDisabled] = useState(false)
  // 大小
  const [size,setSize] =useState('small')

    return(
        <div>
          <button className={classNames('btn',{
            'btn-disabled':disabled,
            'btn-small':size === 'small',
            'btn-large':size === 'large',
          })}>按钮</button>
          <hr/>
          <div>
            操作:
            <button onClick={()=> setDisabled(true)}>禁用</button>
            <button onClick={()=> setSize('small')}>变小</button>
            <button onClick={()=> setSize('large')}>变大</button>
          </div>
        </div>
    )
}
export default App

状态操作表单元素

步骤:

  1. 准备一个状态

    cosnt [value,setValeue] = useState('')
    
  2. 实现数据到是视图的绑定,使用value值绑定状态值

  3. 实现视图到数据的绑定,使用onChange属性设置状态

    <input value={value} onChange={e => setValue(e.target.value)}
    
    • e.target:拿到dom对象
  4. 调用setValue()函数来更新状态

案例:绑定复选框

import { useState } from "react";

const App = () => {
  const [checked, setChecked] = useState(false);
  return (
    <div>
      <input
        type="checkbox"
        checked={checked}
        onChange={(e) => setChecked(e.target.checked)}
      />
      {checked ? "选中了" : "未选中"}
    </div>
  );
};
export default App;

注意⚠️:valueonChange的值需要同时出现,不然没法实现绑定事件.

这种模式在React中有一个专业的术语:受控组件.所谓的受控组件就是收到React状态控制的表单元素.

useRef

在React组件中操作DOM,需要使用useRef,分为两步:

  1. 创建一个ref对象

    const 对象名 = useRef(null)
    
  2. 与JSX绑定

    <input ref={对象名}/>
    
  3. 根据业务进行DOM操作:通过对象名.current.DOM属性拿到DOM对象

案例:

import { useRef } from "react";

const inputRef = useRef(null)

<input ref={inputRef}/>
        <button onClick={()=>alert(inputRef.current.value)}>获取文本框的值</button>

组件通讯

使用场景:一个组件需要使用另一个组件的数据。

根据组件之间的层级关系,常见的React组件通讯分为3种:

  1. 父子组件通讯
  2. 非父子组件通信
  3. 状态管理工具

父子组件通讯

父传子通讯

父组件提供数据,通过props传递给子组件使用。

作用:给组件传递数据,是React组件通讯的基础。

使用步骤:

  1. 传递props:在组件标签上添加属性

    <组件名 定义传递属性="属性值"/>
    
  2. 接受props:通过参数拿到

    const 组件名 = props =>{
    	return(
    		<标签 属性={props.定义传递属性}/>
    	)
    }
    

案例:

const Study = props=>{
  return (
    <div style={{backgroundColor:'red' , width:props.w, height:props.h }}></div>
  )
}
//使用组件
const App = () => {
return (
    <div>
    	<Study w={100} h={200} />
    	{/* <Study w={"100px"} h={"200px"} />  */}
    </div>
  );
};
export default App;

在React中,属性的传递方式有两种:字符串和JavaScript表达式。在上面的例子中,<Study w="100px" h="200px" /><Study w=100 h=200 /> 表现的行为是不同的:

  1. 字符串形式 (<Study w="100px" h="200px" />):
    • 这里的 wh 属性被传递为字符串。即使它们看起来像是数字后面跟着单位(例如 "100px"),它们在JavaScript中被当作字符串处理。
    • 这意味着在组件内部,props.wprops.h 将会是字符串类型的值,包含单位。
  2. JavaScript表达式形式 (<Study w=100 h=200 />):
    • 这里的 wh 属性直接作为JavaScript表达式传递,没有引号。这意味着它们将按照它们在JSX中声明的类型传递给组件。

在组件中如果期望 props.wprops.h 是带有单位的CSS尺寸值。最好还是在设置样式之前将数字转换为带有单位的字符串。

例如,在组件内部添加逻辑来添加单位:

const Study = props => {
  const width = `${props.w}px`; // 假设props.w是数字,这里添加了'px'单位
  const height = `${props.h}px`; // 同上

  return (
    <div style={{backgroundColor: 'red', width, height }}></div>
  );
};

在这个例子中,无论 props.wprops.h 是数字还是字符串,我们都确保它们被转换为带有单位的字符串,以便正确地应用CSS样式。

注意⚠️:这里运用到了模板字符串(也称为模板字面量)的语法。模板字符串允许你嵌入表达式到字符串字面量中,用${}包裹表达式,然后通过插值表达式的值。

简化props

我们还可以对props对象进行简化,由于props是一个对象,我们这里可以直接通过结构的方式直接拿到传过来的值。然后就能删掉props.

const Study = ({w,h})=>{
  return (
    <div style={{backgroundColor:'red' , width:w, height:h }}></div>
  )
}
//使用组件
const App = () => {
return (
    <div>
    	<Study w={100} h={200} />
    </div>
  );
};
export default App;

在实际开发中我们推荐使用简化方式,另外还可以在结构里面添加一个默认值。

const Study = ({w=10,h=20})=>{
  return (
    <div style={{backgroundColor:'red' , width:w, height:h }}></div>
  )
}
//使用组件
const App = () => {
return (
    <div>
    	<Study w={100} h={200} />
    	对比,上面一个是修改了默认值,下面一个是直接默认值
    	<Study/>
    </div>
  );
};
export default App;

image-20240721125251552

子到父通讯

语法模版:

  1. 父组件准备修改数据的函数,传递给子部件

    //父组件
    const 修改数据函数名 = 参数=>{}
    <标签名 修改数据函数名={修改数据函数名}></标签名>	//将修改数据函数传递给子组件
    
  2. 子组件调用函数,将数据作为参数回到父组件

    //子组件
    const 组件名 =({参数,修改数据函数名})=>{
    	return <标签名 onClick ={()=>修改数据函数名(值)}></标签名>
    }	//子组件调用函数
    

案例:

App.css文件:

.container {
  display: flex; /* 设置为Flex容器 */
  align-items: center; /* 垂直居中对齐 */
}
.yes { 
  background-color: #c0e2f0;
  width: 100px;
  height: 30px;
  border: 1px solid black;
  margin:5px;
  text-align: center;
}
.no {
  background-color: #f6c8c8;
}

App.js文件:

import { useState } from "react";
import classNames from "classnames";
import "./App.css";
// 子组件
const Son = ({ id, num,change }) => {
  //子组件调用函数change
  return (
    <div className="container">
      <div className={classNames("yes", id && "no")}>{String(id)}</div>
      <button onClick={() => change(num)}>点击按钮变换颜色</button>
      {/*change(id)将数据作为参数回到父组件 */}
    </div>
  );
};

// 数据
const datas = [
  {
    num: 1,
    id: true,
  },
  {
    num: 2,
    id: true,
  },
  {
    num: 3,
    id: false,
  },
];

// 父组件
const App = () => {
  const [data, setData] = useState(datas); //准备默认值
  const change = (num) => {
    //父组件准备修改数据的函数
    setData(
      data.map((item) => {
        if (item.num === num) {
          return {
            ...item,
            id: !item.id,
          };
        }
        return item
      })
    );
  };
  return (
    <div>
      {data.map((item) => {
        return <Son key={item.num} num= {item.num} id={item.id} change={change} />;
        //遍历渲染 change={change}传递给子部件
      })}
    </div>
  );
};
export default App;

最后实现的案例效果:image-20240721180009982

我们还可以在遍历渲染的时候再次优化代码:

<div>
      {data.map((item) => {
        // return <Son key={item.num} num= {item.num} id={item.id} change={change} />;
        return <Son key={item.num} {...item} change={change} />;
        //{...item}表示数据中的其他属性分别传递给子组件
      })}
    </div>

非父子通讯

兄弟关系

根据组件之间的层级关系,常见的有两种情况:

image-20240721180627027

  1. 找到父组件,提供要共享的数据
  2. 通过父到子通讯,来展示好友名称
  3. 通过子到父通讯,来修改选中的好友

在React中有专门的一个术语来形容:状态提升
如果两个兄弟组件要通讯,就把共享数据提升到公共父组件中

后代关系

React的Context API提供了一种方式,允许你共享那些对于组件树中许多组件都必不可少的数据,例如用户偏好、地区设置、主题等,而不必通过每一层组件手动传递props。

Context(上下文):范围,无视组件层级关系,
跨组件通讯。

  1. 创建Context对象

    const 对象名 = createContext()
    
  2. 划定范围,提供共享数据

    <对象名.Provider value={共享数据}>
    	父组件
    </对象名.Provider>
    
  3. 范围内的组件,获取共享数据

    const 共享数据 = useContext(对象名)
    

案例:共享消息

import React, { createContext, useState, useContext } from 'react';

// 创建Context对象
const MessageContext = createContext();

// 创建Provider组件
const MessageProvider = ({ children }) => {
  const [message, setMessage] = useState('Hello, World!');

  // 更新消息的函数
  const updateMessage = (newMessage) => {
    setMessage(newMessage);
  };

  return (
    <MessageContext.Provider value={{ message, updateMessage }}>
      {children}
    </MessageContext.Provider>
  );
};

// 创建子组件
const MessageDisplay = () => {
  const { message, updateMessage } = useContext(MessageContext);

  return (
    <div>
      <p>{message}</p>
      <button onClick={() => updateMessage('New Message')}>
        Change Message
      </button>
    </div>
  );
};

// 创建另一个子组件
const AnotherComponent = () => {
  const { message } = useContext(MessageContext);

  return (
    <div>
      <h2>Another Component</h2>
      <p>Message from context: {message}</p>
    </div>
  );
};

// 应用的顶层组件
const App = () => {
  return (
    <MessageProvider>
      <div>
        <MessageDisplay />
        <AnotherComponent />
      </div>
    </MessageProvider>
  );
};

export default App;

代码解释:

  1. 创建Context: 使用 createContext 创建一个 MessageContext
  2. 创建Provider: MessageProvider 组件使用 useState 钩子来管理消息状态,并提供了一个 updateMessage 函数来更新消息。它通过 MessageContext.Provider 将这些值提供给子组件。
  3. 创建子组件:
    • MessageDisplay 组件使用 useContext 钩子从 MessageContext 获取消息和更新函数,并显示消息。它还提供了一个按钮来更改消息。
    • AnotherComponent 组件也使用 useContext 钩子从 MessageContext 获取消息,并显示它。
  4. 应用的顶层组件: App 组件使用 MessageProvider 来包裹 MessageDisplayAnotherComponent,确保它们可以访问到Context中的数据。

useEffet的使用

useEffect的作用:在组件生命周期的三个阶段(挂载更新卸载),执行网络请求、浏览器API等操作。这些操作,也叫:副作用😭。

语法:useEffect(Effect函数,依赖项数组)

  • Effect函数:副作用代码
  • 依赖项数组:控制Effect函数的执行时机(可选填)【一般在使用动态数据的时候使用,在更新时用的最多】

生命周期三个阶段:

  • 挂载时:

    useEfect(()=>{
    	//挂载操作代码
    },[])
    
  • 更新时:

    useEffect(()=>{
    	//更新操作代码
    },[依赖项数组])
    

    注意⚠️:更新时调用方式会在组件挂载以及更新时都会执行。

  • 卸载时:

    useEffect(()=>{
    	return()=>{
    		//卸载操作代码
    	}
    },[])
    

    案例:计数器组件

    这个组件将展示一个计数器,每次点击按钮时计数器会增加。同时,组件会在挂载时打印一条消息,在更新时打印另一条消息,并在卸载时清理资源。

    import React, { useState, useEffect } from 'react';
    
    function Counter() {
      const [count, setCount] = useState(0);
    
      // 挂载时执行的副作用
      useEffect(() => {
        console.log('组件挂载');
        return () => {
          console.log('组件卸载');
        };
      }, []); // 空依赖项数组,仅在挂载时执行
    
      // 更新时执行的副作用
      useEffect(() => {
        console.log(`计数器更新为:${count}`);
      }, [count]); // 依赖项数组包含 count,每当 count 更新时执行
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
        </div>
      );
    }
    
    export default Counter;
    

    代码解释:

    1. 状态初始化: 使用 useState 钩子初始化计数器状态 count
    2. 挂载时的副作用: 第一个 useEffect 没有依赖项数组,因此它只在组件挂载时执行一次。它在控制台中打印一条消息,并在组件卸载时执行返回的函数,打印另一条消息。
    3. 更新时的副作用: 第二个 useEffect 依赖于 count。每当 count 更新时,这个 useEffect 都会重新运行,并在控制台中打印新的计数器值。
    4. 渲染UI: 组件渲染一个段落和一个按钮。按钮的点击事件处理器会调用 setCount 函数,从而更新 count 状态。

    这个示例展示了 useEffect 如何在组件的不同生命周期阶段执行代码,以及如何通过依赖项数组控制副作用的执行。

useEffect扩展

推荐:一个useEffect负责一个完整功能

useEffect(()=>{
	//更新操作代码(这里直接用更新来代替挂载)
	return()=>{
		//卸载操作代码
	}
},[依赖项数组])

依赖项说明:什么样的数据才能说明是依赖项呢?

指定依赖项的原则:Effect函数中用到的,并且是可变的值。(例如:props/state/组件中创建的变量等)

以下是一个简单的例子,演示了如何使用 useEffect 来实现一个组件的挂载和卸载操作:

import React, { useState, useEffect } from 'react';

function Clock() {
  // 组件状态,用于显示时间
  const [time, setTime] = useState(new Date().toLocaleTimeString());

  // 使用 useEffect 钩子来处理副作用
  useEffect(() => {
    // 定义一个更新时间的函数
    const updateTime = () => {
      setTime(new Date().toLocaleTimeString());
    };

    // 设置定时器,每秒更新时间
    const timer = setInterval(updateTime, 1000);

    // 这是 effect 的清理函数,会在组件卸载时执行
    return () => {
      clearInterval(timer);
    };
  }, []); // 空的依赖项数组意味着这个 effect 只在挂载时运行一次

  // 渲染时显示当前时间
  return (
    <div>
      The time is: {time}
    </div>
  );
}

export default Clock;

在这个例子中:

  • Clock 组件显示当前的时间,并且每秒更新一次。
  • useState 钩子用于创建 time 状态变量,初始值为当前的本地时间字符串。
  • useEffect 钩子用于设置一个定时器,该定时器每秒调用 updateTime 函数来更新时间。
  • 定时器通过 setInterval 创建,并在 useEffect 的返回函数中清除,以确保在组件卸载时不会继续运行。
  • 依赖项数组是空的 [],这意味着 useEffect 只在组件挂载时运行一次,不会在更新时重新运行。

useEffect应用——发送请求

场景:组件初次渲染时,发送请求获取数据。

步骤:

  1. 调用useEffect,依赖项为空数组
  2. 创建新函数,在该函数上使用async并调用
  3. 发送请求,通过await获取到数据,然后使用
useEffect(()=>{
	const 函数名 = async()=>{
		await
	}
	函数名()	//调用函数
},[])

Json-server

使用json-server提供一个接口

image-20240721224015809

使用axios

安装命令

npm i axios
useEffect(()=>{
	const 函数名 = async()=>{
		const res = await axois get('接口地址')
		setState(res.data)	//更新到状态State里面
	}
	函数名()	//调用函数
},[])

Hooks

ReactHooks是以use开头的函数,比如,useState/useEffect/useContext等。

Hooks(钩子):为组件提供不同的React特性,比如,useStateHook为组件提供状态。

使用规则:只能在组件的顶层调用,不能嵌套在if、for、其他函数中。

原理:React组件依赖Hooks的调用顺序,必须保证所有Hooks,在每次渲染时,调用顺序一致。

image-20240721225454872

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

git sendemail使用

教程参考&#xff1a; git-send-email - 以电子邮件形式发送补丁集 1、安装git-email 2、配置 SMTP 服务器 git config --global sendemail.smtpserver smtp.163.com git config --global sendemail.smtpserverport 465 git config --global sendemail.smtpuser xxxxxx163.c…

Godot入门 04平台设计

新建创景&#xff0c;添加AnimatableBody2D节点。 添加Sprite2D节点 拖动图片 剪裁图片&#xff0c;吸附模式&#xff1a;像素吸附 添加CollisionShape2D&#xff0c;设置实际形状为矩形 重命名AnimatableBody2D节点为Platform&#xff0c;保存场景&#xff0c;拖动platform场景…

Docker NameSpace隔离

1、dd命令&#xff1a;dd 可从标准输入或文件中读取数据&#xff0c;根据指定的格式来转换数据&#xff0c;再输出到文件、设 备或标准输出 功能&#xff1a;用于读取、转换并输出数据 语法&#xff1a;dd OPTION 参数 1、 if文件名&#xff1a;输入文件名&#xff0c;默认为…

【第四天】计算机网络知识 HTTP1.0,HTTP1.1与HTTP2.0的区别 HTTP3.0

HTTP1.0&#xff0c;HTTP1.1与HTTP2.0的区别 HTTP1.0 默认是短链接&#xff0c;可以强制开启长连接。HTTP1.1默认长连接。HTTP2.0采用多路复用。 HTTP1.0&#xff1a; 默认使用短链接&#xff0c;每次请求都需要建立一个TCP连接。它可以设置&#xff1a;Connection: keep-aliv…

内网对抗-隧道技术篇防火墙组策略FRPNPSChiselSocks代理端口映射C2上线

知识点&#xff1a; 1、隧道技术篇-传输层-工具项目-Frp&Nps&Chisel 2、隧道技术篇-传输层-端口转发&Socks建立&C2上线Frp Frp是专注于内网穿透的高性能的反向代理应用&#xff0c;支持TCP、UDP、HTTP、HTTPS等多种协议。可以将内网服务以安全、便捷的方式通过…

H264编码标准环路滤波原理

方块效应产生原因 原因 1&#xff1a;最重要的一个原因是基于块的帧内和帧间预测残差的 DCT 变换。变换系数的量化过程相对粗糙&#xff0c;因而反量化过程恢复的变换系数带有误差&#xff0c;会造成在图像块边界上的视觉不连续。原因 2&#xff1a;其次原因自于运动补偿预测。…

FastAPI(七十九)实战开发《在线课程学习系统》接口开发-- 加入课程和退出课程

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 加入课程 我们先看下加入课程 1.是否登录 2.课程是否存在 3.是否已经存在 4.添加 首先实现逻辑 def get_student_course(db: Session, course: int…

4.Jmeter接口性能测试

性能测试的指标: 多:并发量(用户数-系统用户数-影响到磁盘、在线用户数、并发用户数) 系统用户数:软件系统注册的用户总数(要注意初始化环境) 在线用户数:某段视角内访问用户数,这些用户只是咋先,不一定同时做某一件事情。(初始化环境(warm up热机:让…

DL/T645、IEC104转BACnet网关实现实时数据采集

BA102网关是钡铼技术专为实现电力协议DL/T645、IEC104与楼宇自控协议BACnet相互转化而研发的。它下行采集支持Modbus RTU、Modbus TCP、DL/T645、IEC104等协议&#xff0c;上行转发则支持BACnet IP和BACnet MS/TP协议&#xff0c;从而实现了电力协议与楼宇自控协议之间的相互转…

CJS与ESM:CJS

模块化方案 历史上&#xff0c;JavaScript 一直没有模块&#xff08;module&#xff09;体系&#xff0c;无法将一个大程序拆分成互相依赖的小文件&#xff0c;再用简单的方法拼装起来。其他语言都有这项功能&#xff0c;比如 Ruby 的require、Python 的import&#xff0c;甚至…

【算法】分布式共识Paxos

一、引言 在分布式系统中&#xff0c;一致性是至关重要的一个问题。Paxos算法是由莱斯利兰伯特&#xff08;Leslie Lamport&#xff09;在1990年提出的一种解决分布式系统中一致性问题的算法。 二、算法原理 Paxos算法的目标是让一个分布式系统中的多个节点就某个值达成一致。算…

LoRaWAN设备的两种入网方式(ABP和OTAA)

目录 一、OTAA 1、名词解释 2、入网流程 二、ABP 三、两种入网方式的比较 一、OTAA 1、名词解释 &#xff08;1&#xff09;AppEUI&#xff1a;64位&#xff08;8字节&#xff09;的唯一标识符&#xff0c;用于标识特定的应用程序或组织&#xff08;如果用的是chirpstac…

Linux的yum源安装MySQL5.7

linux的yum源安装MySQL5.7 一、MySQL 1、简介 MySQL 是一种流行的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;由瑞典公司 MySQL AB 开发&#xff0c;后来被 Oracle Corporation 收购。它是一个开源软件&#xff0c;提供了高效、稳定和可靠的数据管理解决…

VLAN与三层交换机

文章目录 VLAN的功能与优势VANE优势基于端口划分 VLAN的优先级最低&#xff0c;是最常用的VLAN划分方式。 VLAN的范围![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/f3f61b8a05ac46a5be263b38816db79f.png)VLAN的帧格式交换网络中的链路类型 交换机的链路类型端口类…

笔试编程算法题笔记(三 C++代码)

1.kotori和n皇后 题意简单来说就是&#xff0c;在一个无穷大的棋盘上&#xff0c;不断插入k个皇后&#xff0c;皇后们如果在 同一行&#xff0c;同一列&#xff0c;在一个45主对角线 在一个135副对角线上&#xff0c;就可以互相攻击。 我们需要判断在第i个皇后插入后&#xff…

Codeforces 903 div3 A-F

A 题目分析 数据范围很小&#xff0c;暴力枚举即可&#xff0c;然后给字符串x的长度设置一个上限&#xff0c;我设了50&#xff0c;因为n*m<25&#xff0c;多一倍够用了 C代码 #include<iostream> using namespace std; void solve(){int n,m;string x,s;cin>>…

视频怎么加密?常见的四种视频加密方法和软件

视频加密是一种重要的技术手段&#xff0c;用于保护视频内容不被未经授权的用户获取、复制、修改或传播。在加密过程中&#xff0c;安企神软件作为一种专业的加密工具&#xff0c;可以发挥重要作用。 以下将详细介绍如何使用安企神软件对视频进行加密&#xff0c;并探讨视频加密…

VUE3学习第三篇:报错记录

1、在我整理好前端代码框架后&#xff0c;而且也启动好了对应的后台服务&#xff0c;访问页面&#xff0c;正常。 2、报错ReferenceError: defineModel is not defined 学到这里报错了 在vue网站的演练场&#xff0c;使用没问题 但是在我自己的代码里就出问题了 3、watchEffec…

等级保护测评解决方案

什么是等级保护测评&#xff1f; 网络安全等级保护是指对国家重要信息、法人和其他组织及公民的专有信息以及公开信息和存储、传输、处理这些信息的信息系统分等级实行安全保护&#xff0c;对信息系统中使用的信息安全产品实行按等级管理&#xff0c;对信息系统中发生的信息安全…

【NPU 系列专栏 3 -- NVIDIA 的 H100 和 H200 的算力介绍】

请阅读【嵌入式及芯片开发学必备专栏】 文章目录 NVIDIA H100 和 H200 的算力NVIDIA H100 芯片的算力NVIDIA H100 算力参数NVIDIA H100 举例 NVIDIA H200 芯片的算力NVIDIA H200 算力参数 H200 的内存和带宽提升H200 推理吞吐量提高H200 性能提升NVIDIA H200 举例Summary NVIDI…