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
就写到这吧嘻嘻嘻嘻西邮元哲来了