REACT学习--钩子剩余部分

useLayouEffect同步执行状态更新

useEffect()是在渲染被绘制到屏幕之后执行的,是异步的,useLayouEffect()是在渲染之后但是在屏幕更新之前,是同步的

大部分情况下我们采用useEffect(),性能更好,但是useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用useLayoutEffect,否则可能会出现闪屏问题

javascript 复制代码
 import { useEffect, useLayoutEffect, useReducer, useState } from "react";

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

    useLayoutEffect(()=>{
        for(let i =0;i<100000;i++){
            setMsg('App hello')
        }
    })
    return (
        <div>
            {msg}
        </div>
    )
 }

 export default App

uselnsertionEffectDOM更新前触发

useInsertionEffect应用场景非常少,因为获取不到DOM元素,所以只在CSS-in-JS 库中才会使用

这是执行的顺序:

javascript 复制代码
import { useEffect,useLayoutEffect,useInsertionEffect } from "react"

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

    return (
        <div>
            hello App
        </div>
    )
}
export default App

突然出bug了问一下deepseek

这大唐笔

不祥的预感

首先 验证依赖安装状态

bash 复制代码
# 进入项目目录
cd /root/NewLife/vite-project

# 检查是否已安装 tailwindcss
ls node_modules | grep tailwindcss

# 如果没有输出结果,执行完整安装
npm uninstall tailwindcss postcss autoprefixer
npm install -D tailwindcss postcss autoprefixer@latest

再强制重建配置文件

bash 复制代码
# 删除现有配置文件
rm -f tailwind.config.js postcss.config.js

# 重新初始化配置
npx tailwindcss init -p --full

我错了对不起

javascript 复制代码
import { useEffect,useLayoutEffect,useInsertionEffect, useRef } from "react"

function App(){
    //触发时机:3 2 1
   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>
            hello App
            <div className="box" ref = {ref}>aaaa</div>
        </div>
    )
}
export default App

Reducer统一的状态管理集合

对于拥有许多状态更新逻辑的组件来说,过于分散的事件可能会令人不知所措,对于这种情况,可以将组件所有状态更新逻辑整合到一个外部函数中,这个函数叫做reducer

Reducer是处理状态的另一种方式

javascript 复制代码
import { useState } from 'react'

function App() {
  const [list, setList] = useState([
    { id: 1, text: 'aaa' },
    { id: 2, text: 'bbb' },
    { id: 3, text: 'ccc' },
  ])
  const handleAdd = () => {
    setList([...list, { id: 4, text: 'ddd' }])
  }
  const handleEdit = (id) => {
    setList(
      list.map((item) => {
        if (item.id === id) {
          return { ...item, text: 'new' + item.text }
        } else {
          return item
        }
      })
    )
  }
  const handleRemove = (id) => {
    setList(
      list.filter((item) => {
        if (item.id === id) {
          return false
        } else {
          return true
        }
      })
    )
  }
  return (
    <div>
      hello App
      <input type="text" />
      <button onClick={() => handleAdd()}>添加</button>
      <ul>
        {list.map((item) => {
            return(
          <li key={item.id}>
            {item.text}
            <button onClick={() => handleEdit(item.id)}>编辑</button>
            <button onClick={() => handleRemove(item.id)}>删除</button>
          </li>
            )
        })}
      </ul>
    </div>
  )
}

export default App

这东西管理起来很麻烦,所以最好集中管理

javascript 复制代码
import { useReducer } from 'react'

//由着reducer函数完成外部逻辑的统一处理
function listReducer(state, action) {
  switch (action.type) {
    case 'add':
      return [...state, { id: 4, text: 'dddd' }]
    case 'edit':
      return state.map((item) => {
        if (item.id === action.id) {
          return { ...item, text: 'new' + item.text }
        } else {
          return item
        }
      })
    case 'remote':
      return state.filter((item) => {
        if (item.id === action.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>
      hello App
      <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: 'remote', id: item.id })}>
                删除
              </button>
            </li>
          )
        })}
      </ul>
    </div>
  )
}

export default App

reducer还提供了一个immer版本

现在来改造一下

useImmerReducer 是结合了 Immer 库和 React useReducer 的增强型 Hook,专为简化不可变状态更新而设计。

javascript 复制代码
import { useImmerReducer } from 'use-immer'

//由着reducer函数完成外部逻辑的统一处理
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 'remote':
        const index = draft.findIndex((item)=>item.id === action.id)
        draft.splice(index,1)
        break
  }
}

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

  return (
    <div>
      hello App
      <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: 'remote', id: item.id })}>
                删除
              </button>
            </li>
          )
        })}
      </ul>
    </div>
  )
}

export default App

Context向组件深层传递数据

通常来说会通过props将信息从父组件传递到子组件,但是如果必须通过许多中间组件向下传递props,或者在应用中的许多组件需要相同的信息,传递props会变得十分的冗长和不便

javascript 复制代码
function App(){
    return (
        <div>
            hello App
            <Head count={123}/>
        </div>
    )
}

function Head({count}){
    return(
        <div>
            hello Head
            <Title count={count}/>
        </div>
    )
}
function Title({count}){
    return (
        <div>
            hello Title,{count}
        </div>
    )
}
export default App

Context允许父组件向其下层无论多深的任何组件提供信息,而无需通过props显式传递

javascript 复制代码
import { createContext,useContext } from "react"

const Context = createContext()
function App(){
    return (
        <div>
            hello App
            <Context.Provider value={123}>
                <Head/>
            </Context.Provider>
        </div>
    )
}

function Head({count}){
    return(
        <div>
            hello Head
            <Title count={count}/>
        </div>
    )
}
function Title({count}){
    const value = useContext(Context)
    return (
        <div>
            hello Title,{value}
        </div>
    )
}
export default App

这样之后就是可以更方便的传递,如果要传递多个值就用数组或者对象

还可以添加默认值

javascript 复制代码
import { createContext,useContext, useState } from "react"

const Context = createContext(0)
function App(){
    const [count,setCount] = useState(123)
    const handleClick=()=>{
        setCount(count+1)
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>点击</button>
            <Context.Provider value={count}>
                <Head/>
            </Context.Provider>
        </div>
    )
}

function Head({count}){
    return(
        <div>
            hello Head
            <Title count={count}/>
        </div>
    )
}
function Title({count}){
    const value = useContext(Context)
    return (
        <div>
            hello Title,{value}
        </div>
    )
}
export default App

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

Reducer可以整合组件的状态更新逻辑,Context可以将信息深入传递给其他组件,可以组合使用他们共同管理一个复杂页面的状态(兄弟状态共享)

存在更复杂的状态管理,可以采用三套方案,Redux,Mobx,Zustand等

ListContent,jsx:

javascript 复制代码
function ListContent(){
    return(
        <ul>
        {
            list.map((item)=> { 
             return(
             <li key={item.id}>{item.text}
             <button onClick={()=>listDispatch({type:'edit',id:item.id})}>编辑</button>
             <button onClick={()=>listDispatch({type:'remove',id:item.id})}>删除</button>
             </li> 
            )
        })}
    </ul>
    )
}
export default ListContent

ListHead.jsx:

javascript 复制代码
import { useContext } from 'react'
import {ListDispatchContext} from './ListProvider.jsx'

function ListHead(){
    const listDispatch = useContext(ListDispatchContext)
    return(
        <>
            <input type="text" /><button onClick={()=>listDispatch({type:'add'})}>添加</button>
        </>
    )
}
export default ListHead

ListProvider.jsx:

javascript 复制代码
import {useReducer,createContext} from 'react'

const ListContext = createContext()
const ListDispatchContext = createContext()

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 'remote':
          const index = draft.findIndex((item)=>item.id === action.id)
          draft.splice(index,1)
          break
    }
  }

function ListProvider({children}){
    const [list,listDispatch] = useReducer(listReducer,[
        {id:1,text:'aaa'},
        {id:2,text:'bbb'},
        {id:3,text:'ccc'},
    ])
    return(
        <ListContext.Provider value={list}>
            <ListDispatchContext.Provider value={listDispatch}>
                {children}
            </ListDispatchContext.Provider>
        </ListContext.Provider>
    )
}

export default ListProvider

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

javascript 复制代码
import ListContent from './ListContent'
import ListHead from './ListHead'
import ListProvider from './ListProvider'

function App() {
  return (
    <div>
      hello App
      <ListProvider>
        <ListHead />
        <ListContent />
      </ListProvider>
    </div>
  )
}
export default App

reducer管理.jsx:

javascript 复制代码
import { useImmerReducer } from 'use-immer'

//由着reducer函数完成外部逻辑的统一处理
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 'remote':
        const index = draft.findIndex((item)=>item.id === action.id)
        draft.splice(index,1)
        break
  }
}

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

  return (
    <div>
      hello App
      <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: 'remote', id: item.id })}>
                删除
              </button>
            </li>
          )
        })}
      </ul>
    </div>
  )
}

export default App

memo在props不变情况下跳过重新渲染

javascript 复制代码
import { useState } from "react"

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

子组件会重新渲染吗?

会的

memo是在props没改变的时候跳过渲染,就可以解决count都没变但是Head还在变的问题

javascript 复制代码
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对计算结果进行缓存

javascript 复制代码
import { useState } from "react";
import { 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.toLowerCase(),msg.toUpperCase()]
    const handleClick =()=>{
        setCount(count + 1)
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>点击</button>
            <Head list={list}/>
        </div>
    )
}

export default App

这个还是会发生改变的,因为地址不同,就算是一样的数组也改变了,他认为是不同的

我们如果想要不让它变

就可以用useMEmo

javascript 复制代码
import { useMemo, useState } from "react";
import { 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 = useMemo(()=>[msg.toLowerCase(),msg.toUpperCase()],[msg])
    const handleClick =()=>{
        setCount(count + 1)
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>点击</button>
            <Head list={list}/>
        </div>
    )
}

export default App

useCallback对函数进行缓存

useMemo的一种特例写法

javascript 复制代码
import { useMemo, useState } from "react";
import { 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 fn=()=>{
    //     console.log(count+1)
    // }
    const fn = useMemo(()=>()=>{},[msg])
    const handleClick =()=>{
        setCount(count + 1)
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>点击</button>
            <Head fn={fn}/>
        </div>
    )
}

export default App

这样使用回调函数也不会变,也可以引入useCallback

javascript 复制代码
import { useMemo, useState,useCallback } from "react";
import { 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 fn=()=>{
    //     console.log(count+1)
    // }
    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

就写到这吧嘻嘻嘻嘻西邮元哲来了

相关推荐
代码搬运媛1 小时前
Jest 测试框架详解与实现指南
前端
吃好睡好便好2 小时前
在Matlab中绘制横直方图
开发语言·学习·算法·matlab
counterxing2 小时前
我把 Codex 里的 Skills 做成了一个 MCP,还支持分享
前端·agent·ai编程
wangqiaowq2 小时前
windows下nginx的安装
linux·服务器·前端
nashane2 小时前
HarmonyOS 6学习:CapsLock键失效诊断与长截图完整实现指南
学习·华为·harmonyos
之歆2 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
发现一只大呆瓜3 小时前
Vite凭什么这么快?3分钟带你彻底搞懂 Vite 热更新的幕后黑手
前端·面试·vite
Maimai108083 小时前
React如何用 @microsoft/fetch-event-source 落地 SSE:比原生 EventSource 更灵活的实时推送方案
前端·javascript·react.js·microsoft·前端框架·reactjs·webassembly
xian_wwq4 小时前
【学习笔记】AGC协调控制系统概述
笔记·学习
kyriewen4 小时前
产品经理把PRD写成“天书”,我用AI半小时重写了一遍,他当场愣住
前端·ai编程·cursor