文章目录
- 两个基础知识
- 1. react的更新问题, react更新会重新执行react函数组件方法本身,并且子组件也会一起更新
- 2. useCallback和useMemo滥用
- useCallback和useMemo要解决什么
- 3. react的state有个经典的闭包,导致拿不到最新数据的问题.
- 常见于useEffect, useMemo, useCallback
- 4. 副作用方案
- 总结
两个基础知识
1. react的更新问题, react更新会重新执行react函数组件方法本身,并且子组件也会一起更新
2. useCallback和useMemo滥用
useCallback和useMemo要解决什么
React.memo包装组件
React.useCallback包裹传递子组件函数
对于传递给组件的对象是固定不变的,和数据没有关系的.
需要将固定变量放在函数外层
React.useMemo: 当你给子组件的对象是和组件数据有关的, 也就是做一个计算属性时候
, 采用useMemo
3. react的state有个经典的闭包,导致拿不到最新数据的问题.
常见于useEffect, useMemo, useCallback
4. 副作用方案
App.jsx
import React, { useState, useRef, useEffect } from 'react'
import Filter from './components/Filter';
import Pagers from './components/Pagers';
import './App.css'
// * 如果这个对象是固定不变的,和state数据无关系, 需要提到组件外侧
const obj = {
a:'cccc'
}
// useRef, 副作用方案
// 如果有一个state数据, 需要传递给子组件时候
// 建议把state数据定义为ref
function App() {
const [count, setCount] = useState(0)
const [filterData, setFilterData] = useState('')
const [pageData, sePageData] = useState('')
// useRef:
// 当有一个变量, 当这个变量不想被外部获取, 不需要写依赖
// 并且还想获取state最新值, 此时,需要将state变为ref
// useRef定义的变量【不会造成视图更新】
// const filterData = useRef('')
// const setFilterData = React.useCallback((val)=>{
// filterData.current = val
// }, [])
// 准备一个请求接口的方法给筛选栏
// 再次更新,就会重新执行, 等于又在内存中新建了一个getList方法
// 第一次渲染->getList 创建 1001 第二次->1002
//1. * useCallback并不是有方法一定要包裹, 只有当这个方法给子组件的时候才有
// const getList = React.useCallback((page)=>{
// console.log('=item=====');
// // console.log(item);
// // *2. useCallback, useEffect, useMemo方法. 如果用到state数据
// // * 一定要把state数据写在第二个数组中, 否则拿不到新数据
// // 改为useRef方式, 搜索栏更新,不会造成page更新
// console.log(count, filterData, page);
// }, [count, filterData])
// 方法3: 使用useEffect监听子组件变化, 不将getList方法传递子组件
useEffect(() => {
console.log(count, filterData, pageData);
}, [count, filterData, pageData])
// * 3. React.useMemo: 当你给子组件的对象是和组件数据有关的, 也就是做一个计算属性时候
return (
<>
<h1>Vite + React</h1>
<div className="card">
父组件本身:
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
</div>
<div>
筛选栏:
<Filter
// config={obj}
filterData={filterData}
setFilterData={setFilterData}
// getList={getList}
/>
</div>
<div className="read-the-docs">
<Pagers
sePageData={sePageData}
// getList={getList}
/>
</div>
</>
)
}
export default App
Pagers.jsx
import React from "react";
function Pagers(props) {
console.log("pager render");
const { sePageData } = props;
return (
<div className="Pager">
{[1, 2, 3, 4, 5].map((i) => {
return (
<div
key={i}
onClick={() => {
sePageData(i);
}}
>
{i}
</div>
);
})}
</div>
);
}
export default React.memo(Pagers);
Filter.jsx
import React, { useState } from 'react'
function Filter(props) {
console.log('Filter render---');
const { setFilterData } = props
const value = React.useRef('')
return (
<>
<input onChange={
(e)=>{
value.current = e.target.value
}
} ></input>
<button onClick={()=> setFilterData(value.current)}>搜索</button>
</>
)
}
export default React.memo(Filter)
总结
小结一下:
React 函数式组件会在state/props/context/父组件重新渲染时,重新执行函数,
为避免不必要的性能消耗(函数重新定义,某些值重新计算等等),减少重新渲染(或者重新执行函数式组件),需要控制props + 配合react.memo
控制props :
- 用useCallback暂存函数(注意用到state时要用好第二个参数)、useMemo避免复杂运算重复执行
- 用Ref(非受控组件)获取不需要渲染,但又是最新的值
- 不变的值写在函数式组外,避免重复创建
- jsx里尽量不写字面量、匿名函数
// useRef:
// 当有一个变量, 当这个变量不想被外部获取, 不需要写依赖
// 并且还想获取state最新值, 此时,需要将state变为ref
// ** useRef定义的变量【不会造成视图更新】**
// const filterData = useRef(‘’)
// const setFilterData = React.useCallback((val)=>{
// filterData.current = val
// }, [])
// 注意:useRef定义的变量【不会造成视图更新】,而state变量会视图更新, 也会被添加到依赖项, 会影响子组件更新
-
使用方案3, 使用useEffect监听子组件变化, 不将getList方法传递子组件
,只用将修改父组件数据的方法传递到子组件. 在父组件通过副作用更新 -
技巧4, 当数据只在子组件中使用时候, 而不会在页面展示时,
需定义为useRef变量,(在页面展示时定义为state变量).
定义在组件内部, 在点击确定时候, 将数据更新到父组件, 修改时用.current修改