目录
React.memo不包裹
React.memo包裹
传递一个简单数据类型
传递一个复杂数据类型
传递一个函数
React.memo不包裹
如果子组件没有使用React.memo包裹,则父组件中数据更新时,子组件会重新进行渲染
父组件:
import { useState } from 'react'
import Son from './Son'
export default function App() {
const [num, setNum] = useState(100)
return (
<>
<h2>App</h2>
<button onClick={() => setNum(num + 1)}>num++ -- {num}</button>
<Son></Son>
</>
)
}
子组件:
export default function Son() {
console.log('子组件触发更新')
return (
<div>
<h3>Son</h3>
</div>
)
}
会发现,在页面初始化加载的时候会触发一次。父组件里状态更新的时候也会触发一次
React.memo包裹
这里主要分析React.memo包裹的情况:
- 正常的话,只有传值给子组件的值(也就是子组件props中的值)发生改变时才会触发子组件渲染
父组件:
import { useState } from 'react'
import Son from './Son'
export default function App() {
const [num, setNum] = useState(100)
return (
<>
<h2>App</h2>
<button onClick={() => setNum(num + 1)}>num++ -- {num}</button>
<Son></Son>
</>
)
}
子组件:
import React from 'react'
export default React.memo(function Son() {
console.log('子组件触发更新')
return (
<div>
<h3>Son</h3>
</div>
)
})
结果:
然后我们依次分析各种传值的情况
传递一个简单数据类型
1. 传值给子组件一个简单数据类型的值:只有值改变的时候子组件才会重新渲染
父组件:
import { useState } from 'react'
import Son from './Son'
export default function App() {
const [num, setNum] = useState(100)
const count = 99
return (
<>
<h2>App</h2>
<button onClick={() => setNum(num + 1)}>num++ -- {num}</button>
<Son count={count}></Son>
</>
)
}
子组件:
import React from 'react'
interface IProps {
count: number
}
export default React.memo(function Son(props: IProps) {
const { count } = props
console.log('子组件触发更新')
return (
<div>
<h3>Son</h3>
</div>
)
})
结果:
传递一个复杂数据类型
2. 传值给子组件一个复杂数据类型的值:
- 如果值使用useState包裹,则只有值改变的时候子组件才会重新渲染
- 如果没有使用useState包裹,则不管值是否变化,只要父组件数据有变化,都会触发子组件重新渲染
不使用useState包裹
父组件:
import { useState } from 'react'
import Son from './Son'
export default function App() {
const [num, setNum] = useState(100)
const list = [1, 2, 3]
return (
<>
<h2>App</h2>
<button onClick={() => setNum(num + 1)}>num++ -- {num}</button>
<Son list={list}></Son>
</>
)
}
子组件:
import React from 'react'
interface IProps {
list: number[]
}
export default React.memo(function Son(props: IProps) {
const { list } = props
console.log('子组件触发更新')
return (
<div>
<h3>Son</h3>
</div>
)
})
结果:
但是这显然不是我们想要的结果,我们使用React.memo包裹子组件,肯定是希望子组件接收的props值发生改变的时候才重新渲染子组件。如果要改善上面的情况,则就需要useMemo包裹下值(useMemo会缓存函数执行之后的值,只要参数2中的依赖项不发生改变,就不会触发参数1的回调函数)或者useState包裹下值
父组件:
import { useMemo, useState } from 'react'
import Son from './Son'
export default function App() {
const [num, setNum] = useState(100)
const list = useMemo(() => [1, 2, 3], [])
return (
<>
<h2>App</h2>
<button onClick={() => setNum(num + 1)}>num++ -- {num}</button>
<Son list={list}></Son>
</>
)
}
子组件:
import React from 'react'
interface IProps {
list: number[]
}
export default React.memo(function Son(props: IProps) {
const { list } = props
console.log('子组件触发更新')
return (
<div>
<h3>Son</h3>
</div>
)
})
结果:
使用useState包裹
父组件:
import { useState } from 'react'
import Son from './Son'
export default function App() {
const [num, setNum] = useState(100)
const [list, setList] = useState([1, 2, 3])
return (
<>
<h2>App</h2>
<button onClick={() => setNum(num + 1)}>num++ -- {num}</button>
<button onClick={() => setList([...list, 99])}>
listChange -- {list}
</button>
<Son list={list}></Son>
</>
)
}
子组件:
import React from 'react'
interface IProps {
list: Array<number>
}
export default React.memo(function Son(props: IProps) {
const { list } = props
console.log('子组件触发更新')
return (
<div>
<h3>Son</h3>
</div>
)
})
结果:
传递一个函数
3. 传值给子组件一个函数:不管函数有没有改变,只要父组件数据状态改变,都会触发子组件重新渲染
父组件:
import { useState } from 'react'
import Son from './Son'
export default function App() {
const [num, setNum] = useState(100)
const fn = () => {
console.log('函数执行')
}
return (
<>
<h2>App</h2>
<button onClick={() => setNum(num + 1)}>num++ -- {num}</button>
<Son fn={fn}></Son>
</>
)
}
子组件:
import React from 'react'
interface IProps {
fn: () => void
}
export default React.memo(function Son(props: IProps) {
const { fn } = props
console.log('子组件触发更新')
return (
<div>
<h3>Son</h3>
</div>
)
})
结果:
同样的,我们使用React.memo包裹子组件,肯定也是希望子组件接收的props值发生改变的时候才重新渲染子组件。如果要改善上面的情况,则就需要useMemo包裹下函数(useMemo会缓存函数执行之后的值,只要参数2中的依赖项不发生改变,就不会触发参数1的回调函数)或者useCallback包裹下函数
父组件:
import { useMemo, useState } from 'react'
import Son from './Son'
export default function App() {
const [num, setNum] = useState(100)
const fn = useMemo(() => {
return () => {
console.log('函数执行')
}
}, [])
return (
<>
<h2>App</h2>
<button onClick={() => setNum(num + 1)}>num++ -- {num}</button>
<Son fn={fn}></Son>
</>
)
}
子组件:
import React from 'react'
interface IProps {
fn: () => void
}
export default React.memo(function Son(props: IProps) {
const { fn } = props
console.log('子组件触发更新')
return (
<div>
<h3>Son</h3>
</div>
)
})
结果:
但是上面使用useMemo,还要return一个函数,写起来很臃肿,就可以使用useCallback这个hook来进行优化,效果是一样的。只需要改变下父组件代码即可
import { useCallback, useState } from 'react'
import Son from './Son'
export default function App() {
const [num, setNum] = useState(100)
const fn = useCallback(() => console.log('函数执行'), [])
return (
<>
<h2>App</h2>
<button onClick={() => setNum(num + 1)}>num++ -- {num}</button>
<Son fn={fn}></Son>
</>
)
}
结果跟上面一样: