文章目录
- 两个基础知识
- 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 (<divkey={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 } = propsconst 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修改