千峰React:Hooks(下)

useLayoutEffect

useLayoutEffect在useEffect之前触发

这样会闪屏,因为是异步的,两次都渲染了

import {useEffect,useState } from 'react';

function App() {
  const [msg,setMsg] = useState('hello App')

  useEffect(() => {
    setMsg('hello useEffect')
  });

  return (
    <div >
      {msg}
    </div>
  );
}

export default App;

换上useLayoutEffect不闪屏,在异步的情况是等两者都执行完以后渲染

import {useLayoutEffect,useState } from 'react';

function App() {
  const [msg,setMsg] = useState('hello App')

  useLayoutEffect(() => {
    setMsg('hello useEffect')
  });

  return (
    <div >
      {msg}
    </div>
  );
}

export default App;

触发多次的时候还是useEffect好,解决闪屏问题用useLayoutEffect

useInsertionEffect Dom

触发顺序是123

import { useEffect, useLayoutEffect, useState } from 'react'

function App() {
  //触发顺序:3->2->1
  useEffect(() => {
    console.log(1)
  })
  useLayoutEffect(() => {
    console.log(2)
  })
  useInsertionEffect(() => {
    console.log(3)
  })

  return <div></div>
}

export default App

有些团队喜欢在js里写样式,这叫CSS-in-Js

有三种实现方法:

useEffect和useLayoutEffect是在jsx渲染后走的样式,会造成浏览器的频繁计算,有性能影响,一般都用useInsertionEffect


import { useInsertionEffect, useRef } from 'react'

function App() {
  const ref = useRef(null)
  
    useInsertionEffect(() => {
        const style = document.createElement('style')
        style.innerHTML = `
        .box{
        background:red;
        width:100px;
        height:100px;
        }`
        document.head.appendChild(style)
  })

    return <div className='box' ref={ref}>哈哈哈</div>
}

export default App

Reducer和Context

Reducer的统一状态管理集合

Reducer是整合状态的工具,我们之前学过useState来组织状态,Reducer可以规划复杂的组件逻辑

内里的逻辑其实就是switch

import { useActionState, useReducer, useState } from 'react'

//由外部函数来完成逻辑操作
function listReducer(state, action) {
  switch (action.type) {
    case 'add':
      return [...state, { id: 4, text: 'ddd' }]
    case 'edit':
      return state.map((item) => {
        if (action.id === item.id) {
          return { ...state, id:action.id,text: 'new ' + item.text }
        } else {
          return item
        }
      })
    case 'remove':
      return state.filter((item) => {
        if (action.id === item.id) {
          return false
        } else return true
      })
  }
}

function App() {
  const [list, dispatch] = useReducer(listReducer, [
    { id: 1, text: 'aaa' },
    { id: 2, text: 'bbb' },
    { id: 3, text: 'ccc' },
  ])

  return (
    <div>
      <input type='text' />
      <button
        onClick={() => {
          dispatch({ type: 'add' })
        }}
      >
        添加
      </button>
      <ul>
        {list.map((item) => {
          return (
            <li key={item.id}>
              {item.text}
              <button
                onClick={() => {
                  dispatch({ type: 'edit', id: item.id })
                }}
              >
                编辑
              </button>
              <button
                onClick={() => {
                  dispatch({ type: 'remove', id: item.id })
                }}
              >
                删除
              </button>
            </li>
          )
        })}
      </ul>
    </div>
  )
}

export default App

之前我们学了Immer可以整合对状态变量的修改,其实useImmerReducer可以实现整合状态变量的修改+起到多个状态管理的作用

这是用immer改写的代码,效果也是一样的

import { useImmerReducer } from 'use-immer'

//由外部函数来完成逻辑操作
function listReducer(draft, action) {
  switch (action.type) {
    case 'add':
      draft.push({ id: 4, text: 'ddd' })
      break
    case 'edit':
      const value = draft.find((item) => item.id === action.id)
      value.text='new '+value.text
      break
    case 'remove':
      const index = draft.findIndex((item) => item.id === action.id)
      draft.splice(index,1)
  }
}

function App() {
  const [list, dispatch] = useImmerReducer(listReducer, [
    { id: 1, text: 'aaa' },
    { id: 2, text: 'bbb' },
    { id: 3, text: 'ccc' },
  ])

  return (
    <div>
      <input type='text' />
      <button
        onClick={() => {
          dispatch({ type: 'add' })
        }}
      >
        添加
      </button>
      <ul>
        {list.map((item) => {
          return (
            <li key={item.id}>
              {item.text}
              <button
                onClick={() => {
                  dispatch({ type: 'edit', id: item.id })
                }}
              >
                编辑
              </button>
              <button
                onClick={() => {
                  dispatch({ type: 'remove', id: item.id })
                }}
              >
                删除
              </button>
            </li>
          )
        })}
      </ul>
    </div>
  )
}

export default App

Context向组件深层传递数据

props通常只能实现父子间的信息传递,Context是跨组件通信的一种方案

比如我们在这里三个组件,实现祖孙级别的通信👇

function Tittle({ count }) {
  return (
    <div>
        Hello Tittle
      {'我是Tittle,这是我接收的参数:'}
      {count}
    </div>
  )
}

function Head({ count }) {
  return (
    <div>
        Hello Head
      <Tittle count={count} />
    </div>
  )
}

function App() {
  return (
    <div>
        Hello App
      <Head count={123} />
    </div>
  )
}
export default App

count通过App->Head,最后传给Tittle

如果使用Context就可以跨组件通信了

import { createContext, useContext } from 'react'
const Context = createContext()//定义钩子,尽量大写

function Tittle() {
  const value = useContext(Context)//定义接收的参数
  return (
    <div>
      Hello Tittle
      {'我是Tittle,这是我接收的参数:'}
      {value}
    </div>
  )
}

function Head() {
  return (
    <div>
      Hello Head
      <Tittle />
    </div>
  )
}

function App() {
  return (
    <div>
      Hello App
      <Context.Provider value={123}>'{提供数据}'
        <Head />
      </Context.Provider>
    </div>
  )
}
export default App

这里传递的参数只有一个值,如果传递多个可以写成数组或者对象:

function App() {
  return (
    <div>
      Hello App
      <Context.Provider value={[1,2,3,4,5]}>
        <Head />
      </Context.Provider>
    </div>
  )
}
import { createContext, useContext } from 'react'
const Context = createContext()

function Tittle() {
  const value = useContext(Context)
  return (
    <div>
      Hello Tittle
      {'我是Tittle,这是我接收的参数:'}
      {'id:' + value.id}
      {'text:' + value.text}
    </div>
  )
}

function Head() {
  return (
    <div>
      Hello Head
      <Tittle />
    </div>
  )
}

function App() {
  return (
    <div>
      Hello App
      <Context.Provider value={{id:1,text:'aaa'}}>
        <Head />
      </Context.Provider>
    </div>
  )
}
export default App

但是不管传递多少个数据,只能通过value来传递

跨组件传递的状态变量,也会在渲染的时候重新传递、重新渲染

Reducer配合Context实现共享状态管理

实现父子通信用props,实现深层通信用context,实现兄弟间通信怎么做?

一个方法是使用状态提升,把状态提到两个兄弟的公共区域,再从公共区域通过父子通信的方式传回去,但是不够灵活

太复杂的可以用第三方库

但是React里,把这俩玩意一起用,Context可以跨组件通信,Reducer可以整合组件更新逻辑,也可以实现兄弟通信

六月份学长说建议以后使用redux,反正我也看不懂,就这样吧

memo

我们在父组件里调用子组件,父组件有改变,子组件内部无需渲染的情况下,其实还是会渲染的👇

可以看见子组件里面的随机数在改变

为了节省性能,可以使用memo来优化效率

import { useState,memo } from 'react'

const Head = memo(function Head() {
  return (
    <div>
      Hello Head,{ Math.random()}
    </div>
  )
}
)
function App() {
  const [count, setCount] = useState(0)
  const handleClick = () => {
    setCount(count + 1)
  }

  return (
    <div>
      Hello App
      <button onClick={handleClick}></button>
        <Head />
    </div>
  )
}
export default App

可以看见随机数并没有改变

useMemo对计算结果进行缓存

import { useState,memo } from 'react'

const Head = memo(function Head() {
  return (
    <div>
      Hello Head,{ Math.random()}
    </div>
  )
}
)
function App() {
  const [count, setCount] = useState(0)
  const [msg, setMsg] = useState('hello React')
  const list=[msg.toLocaleLowerCase(),msg.toLocaleUpperCase()]
  const handleClick = () => {
    setCount(count + 1)
  }

  return (
    <div>
      Hello App
      <button onClick={handleClick}></button>
        <Head list={list} />
    </div>
  )
}
export default App

按理来说,Head加了memo应该是不会重新刷新的对吧

但是其实刷新两次,随机数还是不同的

因为引用类型的判别(之前说过)

这时候要用useMemo来解决引用类型在Object.Is()方法被判定为不同导致重新渲染耗费性能的问题

useMemo是靠缓存上次结果,拿上次结果做对比的

import { useState,memo,useMemo } from 'react'

const Head = memo(function Head() {
  return (
    <div>
      Hello Head,{ Math.random()}
    </div>
  )
}
)
function App() {
  const [count, setCount] = useState(0)
  const [msg, setMsg] = useState('hello React')
  const list = useMemo(() => [msg.toLocaleLowerCase(),msg.toLocaleUpperCase()])//只有msg被修改时,list才会重新计算,其他情况都是拿上次缓存的值
  
  const handleClick = () => {
    setCount(count + 1)
  }

  return (
    <div>
      Hello App
      <button onClick={handleClick}></button>
        <Head list={list} />
    </div>
  )
}
export default App

useCallback对函数进行缓存

上面的代码我们引用的是数组,这里写一个函数:


import { useState,memo,useMemo } from 'react'

const Head = memo(function Head() {
  return (
    <div>
      Hello Head,{ Math.random()}
    </div>
  )
}
)
function App() {
  const [count, setCount] = useState(0)
  const [msg, setMsg] = useState('hello React')
  const fn = () => {
    console.log(msg)
  }
  //const list = useMemo(() => [msg.toLowerCase(),msg.toUpperCase()],[msg])//只有msg被修改时,list才会重新计算,其他情况都是拿上次缓存的值
  
  const handleClick = () => {
    setCount(count + 1)
  }

  return (
    <div>
      Hello App
      <button onClick={handleClick}>点我</button>
        <Head fn={fn} />
    </div>
  )
}
export default App

还是会多渲染,所以我们用useCallback,useCallback其实就是省略了useMemo里面套两个回调的写法:

import { useState,memo,useMemo, useCallback } from 'react'

const Head = memo(function Head() {
  return (
    <div>
      Hello Head,{ Math.random()}
    </div>
  )
}
)
function App() {
  const [count, setCount] = useState(0)
  const [msg, setMsg] = useState('hello React')
  const fn = useCallback(() => {
    console.log(msg)
  },[msg])
  const handleClick = () => {
    setCount(count + 1)
  }

  return (
    <div>
      Hello App
      <button onClick={handleClick}>点我</button>
        <Head fn={fn} />
    </div>
  )
}
export default App

startTransition方法及并发模式

我们react居然还有并发

import { useState,memo,useMemo, useCallback, startTransition } from 'react'

function List({ query}) {
  
  const items = []
  const word = 'hello World'
  if (query !== '' && word.includes(query)) {
    const arr = word.split(query)
    for (let i = 0; i < 1000; i++) {
      items.push(<li key={i}>{arr[0]}<span style={{color:'red'}}>{query}</span></li>)
    }
  } else {
    for(let i=0;i<1000;i++){
      items.push(<li key={i}>{ word}</li>)
    }
  }
  return (
    <ul>
      {items}
    </ul>
  )
}

function App() {
  const [search, setSearch] = useState('')
  const [query,setQuery]=useState('')
  const handleChange = (e) => {
    //紧急
    setSearch(e.target.value)
    //将这个任务设置为非紧急
    startTransition(() => { 
    setQuery(e.target.value)})
  }

  return (
    <div>
      Hello App
      <input type="text" value={search} onChange={handleChange} />
      <List query={ query} />
    </div>
  )
}
export default App

设计成非紧急任务以后,就先在输入框流畅的显示输入的字符串,再染色

如果两个都是紧急任务的话,就得等两个都完成

useTransition与useDeferredValue

在加载的时候显示loading

import { useState,memo,useMemo, useCallback, startTransition ,useTransition} from 'react'

function List({ query}) {
  
  const items = []
  const word = 'hello World'
  if (query !== '' && word.includes(query)) {
    const arr = word.split(query)
    for (let i = 0; i < 1000; i++) {
      items.push(<li key={i}>{arr[0]}<span style={{color:'red'}}>{query}</span></li>)
    }
  } else {
    for(let i=0;i<1000;i++){
      items.push(<li key={i}>{ word}</li>)
    }
  }
  return (
    <ul>
      {items}
    </ul>
  )
}

function App() {
  const [search, setSearch] = useState('')
  const [query, setQuery] = useState('')
  const [pending, startTransition] = useTransition()
  const handleChange = (e) => {
    //紧急
    setSearch(e.target.value)
    //将这个任务设置为非紧急
    startTransition(() => { 
    setQuery(e.target.value)})
  }

  return (
    <div>
      Hello App
      <input type="text" value={search} onChange={handleChange} />
      {pending && <div>loading...</div>}
      <List query={ query} />
    </div>
  )
}
export default App

useDeferredValue的使用

import { useState,memo,useMemo, useCallback, startTransition ,useTransition, useDebugValue, useDeferredValue} from 'react'

function List({ query}) {
  
  const items = []
  const word = 'hello World'
  if (query !== '' && word.includes(query)) {
    const arr = word.split(query)
    for (let i = 0; i < 1000; i++) {
      items.push(<li key={i}>{arr[0]}<span style={{color:'red'}}>{query}</span></li>)
    }
  } else {
    for(let i=0;i<1000;i++){
      items.push(<li key={i}>{ word}</li>)
    }
  }
  return (
    <ul>
      {items}
    </ul>
  )
}

function App() {
  const [search, setSearch] = useState('')
  const query = useDeferredValue(search)
  const handleChange = (e) => {
    //紧急
    setSearch(e.target.value)
  }

  return (
    <div>
      Hello App
      <input type="text" value={search} onChange={handleChange} />
      <List query={query} />
    </div>
  )
}
export default App

useId生成唯一id值

很少用,会在一些无障碍的操作用到

如果两次调用同一个组件,会发现这两个组件的id一样

function MyInput() {
  return (
    <div>
      <label>密码:<input type="password" aria-describedby="password" /></label>
      <p id="password">密码至少包含18个字符</p>
    </div>
  )
}

function App() {
  return (
    <div>
      Hello App
      <MyInput />
      <MyInput />
    </div>
  );
}

export default App;

id相同👇但是最好不应该相同

import { useId } from 'react-id-generator'

function MyInput() {
  const password = useId()
  return (
    <div>
      <label>密码:<input type="password" aria-describedby={password} /></label>
      <p id={password}>密码至少包含18个字符</p>
    </div>
  )
}

function App() {
  return (
    <div>
      Hello App
      <MyInput />
      <MyInput />
    </div>
  );
}

export default App;

生成两个不同的id

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

盛京开源社区加入 GitCode,书写东北开源生态新篇章

在数字化转型与开源技术蓬勃发展的浪潮下&#xff0c;开源社区已成为推动技术创新的核心力量。盛京开源社区&#xff08;SJOSC&#xff09;作为沈阳地区的开源交流平台&#xff0c;始终致力于连接开发者、企业及高校&#xff0c;构建区域技术生态圈。 现在&#xff0c;盛京开源…

Unity:实时查看和调试日志信息(In-game Debug Console插件)

在Unity中使用In-game Debug Console插件可以方便地在应用内实时查看和调试日志信息。 1、导入插件 从Packages:My Assets导入In-game Debug Console插件&#xff0c;导入后&#xff0c;插件会自动添加到项目的Packages文件夹中。&#xff08;需要先下载该插件&#xff09; 2、…

SQL Server的安装和简单使用

目录 一、SQL Server 1.1、简介 1.2、安装包 二、安装SQL Server 2.1、双击安装包 2.2、选择自己想要安装的位置 2.3、点击安装 2.4、安装完成之后会出现以下页面&#xff0c;按照序号依次点击 2.5、不用管密钥&#xff0c;点击下一步 2.6、选择【我接受】 2.7、是否…

Cursor+pycharm接入Codeuim(免费版),Tab自动补全功能平替

如题&#xff0c;笔者在Cursor中使用pycharm写python程序&#xff0c;试用期到了Tab自动补全功能就不能用了&#xff0c;安装Codeuim插件可以代替这个功能。步骤如下&#xff1a; 1. 在应用商店中搜索扩展Codeuim&#xff0c;下载安装 2. 安装完成后左下角会弹出提示框&#x…

<tauri><rust><GUI>基于tauri,实现websocket通讯程序(右键菜单、websocket)

前言 本文是基于rust和tauri&#xff0c;由于tauri是前、后端结合的GUI框架&#xff0c;既可以直接生成包含前端代码的文件&#xff0c;也可以在已有的前端项目上集成tauri框架&#xff0c;将前端页面化为桌面GUI。 环境配置 系统&#xff1a;windows 10平台&#xff1a;vis…

【官方配图】win10/win11 安装cuda 和 cudnn

文章目录 参考资料1.安装cuda toolkit1. 下载安装包2.安装验证 2. 安装cudnn下载cudnn安装包安装cudnn安装后的配置 参考资料 官方nvidia安装cuda官方nvidia安装cudnn 1.安装cuda toolkit 1. 下载安装包 下载地址 https://developer.nvidia.com/cuda-downloads?target_osW…

【监督学习】K 邻近算法步骤及matlab实现

K 邻近算法 &#xff08;三&#xff09;K 邻近算法1.算法步骤2. MATLAB 实现参考资料 &#xff08;三&#xff09;K 邻近算法 K 近邻算法&#xff08;KNN&#xff0c;K-Nearest Neighbors&#xff09;是一种简单且直观的监督学习方法&#xff0c;可用于分类和回归任务。它的工…

音视频-WAV格式

1. WAV格式说明&#xff1a; 2. 格式说明&#xff1a; chunkId&#xff1a;通常是 “RIFF” 四个字节&#xff0c;用于标识文件类型。&#xff08;wav文件格式表示&#xff09;chunkSize&#xff1a;表示整个文件除了chunkId和chunkSize这 8 个字节外的其余部分的大小。Forma…

学习threejs,使用ShaderMaterial自定义着色器材质

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.ShaderMaterial1.1.1…

Selenium自动化测试框架快速搭建

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、介绍 Selenium目前主流的web自动化测试框架&#xff1b;支持多种编程语言Java、pythan、go、js等&#xff1b;selenium 提供一系列的api 供我们使用&#xf…

【文献阅读】A Survey on Hardware Accelerators for Large Language Models

大语言模型硬件加速器综述 大语言模型&#xff08;LLMs&#xff09;已成为自然语言处理任务的强大工具&#xff0c;凭借其理解和生成类似人类文本的能力&#xff0c;彻底改变了该领域。随着对更复杂大语言模型的需求持续增长&#xff0c;迫切需要应对与其规模和复杂性相关的计…

机器幻觉产生的原因

机器幻觉是指模型生成的不符合现实的内容&#xff0c;比如图像生成中的错误或者不合理的输出。 线性函数在神经网络中的作用通常是传递梯度&#xff0c;但如果每一层都是线性的&#xff0c;整个网络就相当于一个单层的线性模型&#xff0c;无法学习复杂的模式。所以如果只有线性…

python-leetcode-颜色分类

75. 颜色分类 - 力扣&#xff08;LeetCode&#xff09; class Solution:def sortColors(self, nums: List[int]) -> None:"""Do not return anything, modify nums in-place instead."""low, mid, high 0, 0, len(nums) - 1while mid < h…

如何使用LLDB 在VSCode调试C++代码

LLDB VSCode调试 第一步.拷贝lldb-server到android系统 adb push ${NDK_PATH}/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/9.0.9/lib/linux/arm/lldb-server /data/local/tmp/lldb-server第二步.进入到安卓设备&#xff0c;打开lldb-server adb shell cd /data/lc…

2025中建二测笔试考什么?北森题库考点复习|附精华备考面试攻略

大家好&#xff0c;我是职小豚&#xff0c;将为大家详细解析2025年中建二测的笔试内容&#xff0c;并提供备考面试的全方位攻略。 希望这份指南能帮助大家在求职路上更加顺利&#xff01; 一、中国建筑集团公司介绍 中国建筑集团有限公司&#xff08;简称“中建集团”&#…

GD32F450 使用

GB32F450使用 1. 相关知识2. 烧写程序3. SPI3.1 spi基础3.2 spi代码 4. 串口4.1 串口引脚4.2 串口通信代码 问题记录1. 修改晶振频率 注意&#xff1a;GD32F450 总共有三种封装形式&#xff0c;本文所述的相关代码和知识&#xff0c;均为 GD32F450IX 系列。 1. 相关知识 参数配…

Spring Boot 测试:单元、集成与契约测试全解析

一、Spring Boot 分层测试策略 Spring Boot 应用采用经典的分层架构&#xff0c;不同层级的功能模块对应不同的测试策略&#xff0c;以确保代码质量和系统稳定性。 Spring Boot 分层架构&#xff1a; Spring Boot分层架构 A[客户端] -->|HTTP 请求| B[Controller 层] …

(十 三)趣学设计模式 之 模版方法模式!

目录 一、 啥是模板方法模式&#xff1f;二、 为什么要用模板方法模式&#xff1f;三、 模板方法模式的实现方式四、 模板方法模式的优缺点五、 模板方法模式的应用场景六、 总结 &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博主的讲解方式&a…

汽车刹车系统设计

摘 要 本次设计内容为汽车刹车系统&#xff0c;其可靠性与驾驶人的生命息息相关&#xff0c;是汽车所有组成部分中最重要的一环。刹车系统是在车辆行驶过程中出现紧急情况时首先保护车辆与驾驶人员安全的反应系统&#xff0c;工作原理是依靠制动装置工作时产生的大量摩擦力来抵…

卷积神经网络梯度下降方向与参数更新方向的一致性论述

梯度下降是一种常用的优化算法&#xff0c;用于最小化损失函数&#xff0c;在机器学习和深度学习领域有着广泛的应用。分别对梯度下降、梯度方向以及参数更新采用负梯度方向的原因进行论述。 1.梯度下降 它的基本思想是通过迭代的方式来更新模型的参数&#xff0c;使得损失函数…