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

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

相关推荐
niandb2 小时前
The Rust Programming Language 学习 (一)
linux·c语言·c++·学习·rust
web150854159353 小时前
Spring Cloud Gateway
android·前端·后端
IT、木易3 小时前
大白话html第五章HTML5 新增表单元素和属性
前端·html·html5
一个处女座的程序猿O(∩_∩)O3 小时前
前端正则表达式完全指南:从入门到实战
前端·正则表达式
正在绘制中5 小时前
Go语言学习笔记(四)
笔记·学习·golang
安静的小员5 小时前
IPv4应用场景API:精准识别IP属性,赋能业务决策
java·开发语言·前端·javascript·后端
Ttang236 小时前
JavaWeb基础专项复习6——AJAX
xml·java·开发语言·前端·javascript·ajax
麗o麗6 小时前
C语言整体梳理-基础篇-预处理指令
java·c语言·前端
拉不动的猪6 小时前
刷刷题21(常见面试题)
前端·javascript·面试