react re-render的解决方案
2023-12-13 17:31:13
问题代码
import {Dispatch, FC, SetStateAction, useState} from 'react'
import './App.css'
const Child: FC<{ m: number, setM: Dispatch<SetStateAction<number>> }> = (props) => {
const {m, setM } = props
return (
<div>
<button onClick={() => setM(m + 1)}>addM</button>
<span>child</span>
<span>{m}</span>
</div>
)
}
const App = () => {
const [n, setN] = useState(0)
const [m, setM] = useState(0)
return (
<div>
<button onClick={() => setN(n + 1)}>addN</button>
<span>father</span>
<span>{n}</span>
<div>
<Child m={m} setM={setM}/>
</div>
</div>
)
}
export default App
问题描述
上面这个组件现在存在的问题:
当我们在点击n+1时,会导致App组件重新渲染,然后Child组件虽然不依赖n,但是由于父组件re-render,他自己也会进行无效的re-render
解决方法一 使用Memo
为了减少这种无效的re-render,我们经常会使用memo()去包裹组件,配合useCallback缓存函数,useMemo缓存其他变量来达到缓存组件,减少无效更新的情况。
import {Dispatch, FC, memo, SetStateAction, useCallback, useState} from 'react'
import './App.css'
const _Child: FC<{ m: number, setM: Dispatch<SetStateAction<number>> }> = (props) => {
const {m, setM } = props
console.log('child render')
return (
<div>
<button onClick={() => setM(m + 1)}>addM</button>
<span>child</span>
<span>{m}</span>
</div>
)
}
const Child= memo(_Child)
const App = () => {
const [n, setN] = useState(0)
const [m, setM] = useState(0)
return (
<div>
<button onClick={() => setN(n + 1)}>addN</button>
<span>father</span>
<span>{n}</span>
<div>
<Child m={m} setM={useCallback(setM, [])}/>
</div>
</div>
)
}
解决方法二 向下移动state
这个解决方法其实就是将组件粒度变得更细。
将state下沉到与之相关的组件中去,也就是将与该状态相关的组件抽离成一个单独的组件。
import {useState} from 'react'
import './App.css'
const Child = () => {
const [m, setM] = useState(0)
return (
<div>
<button onClick={() => setM(m + 1)}>addM</button>
<span>child</span>
<span>{m}</span>
</div>
)
}
const Child2 = () => {
const [n, setN] = useState(0)
return (
<>
<button onClick={() => setN(n + 1)}>addN</button>
<span>Child2</span>
<span>{n}</span>
</>
)
}
const App = () => {
return (
<div>
<div>
<Child2/>
</div>
<div>
<Child/>
</div>
</div>
)
}
export default App
解决方法三 内容提升
像上面那种情况,组件可以单独抽离是因为知道Child组件不依赖n的状态 代码如下。
但是如果我们假设是App中的div依赖n呢。这种情况下其实Child组件依然不应该刷新
import {Dispatch, FC, SetStateAction, useState} from 'react'
import './App.css'
const Child: FC<{ m: number, setM: Dispatch<SetStateAction<number>> }> = (props) => {
const {m, setM } = props
return (
<div>
<button onClick={() => setM(m + 1)}>addM</button>
<span>child</span>
<span>{m}</span>
</div>
)
}
const App = () => {
const [n, setN] = useState(0)
const [m, setM] = useState(0)
return (
<div class={n.toString()}> //父组件依赖n
<button onClick={() => setN(n + 1)}>addN</button>
<span>father</span>
<span>{n}</span>
<div>
<Child m={m} setM={setM}/>
</div>
</div>
)
}
export default App
解决上面代码的问题
将于n相关的组件放到Father中,然后不相关的作为children传给Father组件。
这样在Father组件re-render的时候,由于App(父组件)中的组件没有变化,所以拿到的children依然是上一次的(没有发生变化的)所以children部分不会re-render。
这样就避免了无效的刷新
import {FC, HTMLAttributes, useState} from 'react';
export default function App() {
return (
<Father>
<Child/>
</Father>
);
}
// 将内容提升到该父组件中
interface Props extends HTMLAttributes<HTMLDivElement>{}
const Father:FC<Props>=({children})=> {
const [n, setN] = useState(0)
return (
<div>
<button onClick={() => setN(n + 1)}>addN</button>
<span>father</span>
<span>{n}</span>
<div>
{children}
</div>
</div>
)
}
const Child = () => {
const [m, setM] = useState(0)
console.log("执行了")
return (
<div>
<button onClick={() => setM(m + 1)}>addM</button>
<span>child</span>
<span>{m}</span>
</div>
)
}
解决方法四 内容集中
import {useState} from 'react'
import './App.css'
const App = () => {
const [n, setN] = useState(0)
const [m, setM] = useState(0)
return (
<div className={n.toString()}>
<div>
<button onClick={() => setN(n + 1)}>addN</button>
<span>father</span>
<span>{n}</span>
</div>
// 将内容提升到该父组件中
<div>
<button onClick={() => setM(m + 1)}>addM</button>
<span>child</span>
<span>{m}</span>
</div>
</div>
)
}
export default App
文章来源:https://blog.csdn.net/weixin_46787337/article/details/134907939
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!