性能优化相关 hook
React.memo
当父组件被重新渲染的时候,子组件也会触发渲染,这样就多了无意义的性能开销,如果子组件的状态没有发生变化,则子组件是必须要被重新渲染的。 在 React Hooks 中我们可以使用 React.memo 来解决上述问题从而提高性能的目的。
const 组件 = React.memo(函数式组件)
tsx
import React, { useEffect, useState } from "react";
const Father: React.FC = () => {
const [count, setCount] = useState(0);
const [name, setName] = useState("xiaoming");
const add = () => {
setCount(count + 1);
};
const changeName = () => {
setName("1235");
};
return (
<>
<div>count: {count}</div>
<div>name: {name}</div>
<button onClick={add}>+1</button>
<button onClick={changeName}>changeName</button>
<Son count={count} />
</>
);
};
const Son: React.FC<{ count: number }> = React.memo((props) => {
useEffect(() => {
console.log("Son组件渲染");
});
return <div>Son: {props.count}</div>;
});
export default Father;
useMemo
用于缓存变量,返回的是值
语法格式如下:
tsx
const memorizeValue = useMemo(cb, array);
const memoValue = useMemo(() => {
return 计算后的值;
}, [value]); // 表示监听的value的变化
其中:
- cb:这是一个函数,用户处理计算的逻辑,必须使用 return 返回计算的结果
- array: 这个数组中存储的是依赖项,只要依赖项发生变化都会触发 cb 的重新执行。使用 array 需要注意以下几点
- 不传数组,每次更新都会重新计算
- 空数组,只会计算一次
- 依赖对应的值变化时会重新执行 cb
示例:
tsx
const Father = () => {
const [flag, setFlag] = useState(true);
const [count, setCount] = useState(0);
const add = () => {
setCount(count + 1);
};
const changeFlag = () => {
setFlag(!flag);
};
const tips = useMemo(() => {
console.log("tips重新渲染");
return flag ? (
<p>哪里贵了,不要睁着眼瞎说好不好</p>
) : (
<p>这些年有没有努力工作,工资涨没涨</p>
);
}, [flag]);
return (
<>
<div>flag: {flag.toString()}</div>
<div>
<button onClick={changeFlag}>changeFlag</button>
</div>
{tips}
<div>count: {count}</div>
<button onClick={add}>+1</button>
</>
);
};
useCallback
用于缓存函数,返回的是函数
语法格式如下:
tsx
const memoCallback = useCallback(cb, array);
useCallback 会返回一个 memorized 回调函数供组件使用,从而防止组件 rerender 的时候反复创建相同的函数,能够节省内存的开销,提高性能。
其中:
- cb:这是一个函数,用户处理业务逻辑,必须使用 return 返回计算的结果
- array: 这个数组中存储的是依赖项,只要依赖项发生变化都会触发 cb 的重新执行。使用 array 需要注意以下几点
- 不传数组,每次更新都会重新计算
- 空数组,只会计算一次
- 依赖对应的值变化时会重新执行 cb
tsx
const set = new Set();
const Search = () => {
const [keywords, setKeywords] = useState("");
const change = (e: React.ChangeEvent<HTMLInputElement>) => {
setKeywords(e.target.value);
};
set.add(change);
console.log(set.size); // 当输入的时候keywords改变 触发组件的渲染,使用set收集来验证是否函数会重复声明,结果是 每次输入东西都会触发重新创建函数
return (
<>
<input type="text" value={keywords} onChange={change} />
<div>keywords: {keywords}</div>
</>
);
};
export default Search;
✨ 注意: 当使用 change 时间的时候在 ts 中需要使用
React.ChangeEvent<HTMLInputElement>
,其中的泛型需要根据当前的控件来确定
使用 useCallback 来解决当前问题
tsx
const set = new Set();
const Search = () => {
const [keywords, setKeywords] = useState("");
const change = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setKeywords(e.target.value);
}, []); // 因为当前函数只需要声明一次,所以使用空数组
set.add(change);
console.log(set.size); // 每次打印都是1
return (
<>
<input type="text" value={keywords} onChange={change} />
<div>keywords: {keywords}</div>
</>
);
};
export default Search;
案例:
tsx
type SearchInputType = {
changeKw: (e: React.ChangeEvent<HTMLInputElement>) => void;
};
const Search = () => {
const [kw, setKw] = useState("");
const changeKw = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setKw(e.target.value);
}, []);
return (
<>
<div>kw: {kw}</div>
<SearchInput changeKw={changeKw} />
<SearchResult kw={kw} />
</>
);
};
const SearchInput: React.FC<SearchInputType> = React.memo((props) => {
useEffect(() => {
console.log("SearchInput 组件渲染");
});
return (
<>
<input type="text" onChange={props.changeKw} />
</>
);
});
type ResultType = {
id: number;
word: string;
};
const SearchResult: React.FC<{ kw: string }> = (props) => {
const [result, setResult] = useState<ResultType[]>([]);
useEffect(() => {
if (props.kw) {
fetch("http://api.liulongbin.top/v1/words?kw=" + props.kw)
.then((res) => res.json())
.then((res) => {
console.log(res.code);
if (res.code === 0) {
setResult(res.data);
} else {
setResult([]);
}
});
} else {
}
}, [props.kw]);
return (
<>
{result.map((item) => {
return <div key={item.id}>{item.word}</div>;
})}
</>
);
};