React Hooks 学习笔记
- useEffect
接受两个参数:
- 第一个参数是函数(这里叫effect函数),它的作用是,在页面渲染后执行这个函数。因此你可以把ajax请求等放在这里执行;
- 第二个参数是一个数组,这里注意:
参数情况 | 效果 | 注意 |
不传 | 每次渲染后都执行清理或者执行effect | 这可能会导致性能问题,比如两次渲染的数据完全一样 |
传空数组 | 只运行一次的 effect(仅在组件挂载和卸载时执行) | 这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行 |
传[count] | React 将对前一次渲染的count和后一次渲染的count进行比较。若相等React 会跳过这个 effect | 实现了性能的优化 |
import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState({}); useEffect(() => { setCount({test:"count是一个对象,使得页面死循环"}) },[count]); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
{} === {}
结果是false,{a:1} === {a:1}
同样,由此造成了react以为两个值不同,就一直的渲染最终页面死循环。- lodash.debounce
useEffect中的debounce和setInterval中的debounce有点不同: useEffect中的debounce 因为状态发生的改变,所以之前的debounce就不是之前的debounce了,
而是最新的debounce,所以一定会执行 但setInterval中的debounce 还是同一个debounce,所以不会执行
- useMemo和useCallback
众所周知,react中,性能的优化点在于:
- 调用setState,就会触发组件的重新渲染,无论前后的state是否不同
- 父组件更新,子组件也会自动的更新, (无论是state变化还是修改 redux中的数据)
基于上面的两点,我们通常的解决方案是:使用immutable进行比较,在不相等的时候调用setState;在shouldComponentUpdate中判断前后的props和state,如果没有变化,则返回false来阻止更新。
在hooks出来之后,我们能够使用function的形式来创建包含内部state的组件。但是,使用function的形式,失去了上面的shouldComponentUpdate,我们无法通过判断前后状态来决定是否更新。而且,在函数组件中,react不再区分mount和update两个状态,这意味着函数组件的每一次调用都会执行其内部的所有逻辑,那么会带来较大的性能损耗。因此useMemo 和useCallback就是解决性能问题的杀手锏。
useMemo和useCallback都会在组件第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行;并且这两个hooks都返回缓存的值,useMemo返回缓存的变量,useCallback返回缓存的函数。
在useEffect地第二个参数中,只需将参数使其包裹起来后,就可以利用引用地址来减少重复渲染了
import React, {useState, useMemo, memo, useCallback } from 'react';
import ReactDOM from 'react-dom';
import './App.css';
let Child = ({data, handleClick}) => {
// 父组件更新, 子组件即使没变化也更新
console.log('%c让我康康 Child Render', 'border-radius:3px;background:#000;color:#f6e387;padding: 2px 5px');
return (
<button onClick={handleClick}>
{data.number}
</button>
)
}
Child = memo(Child);
// let memoData = {num:10};
// let onClick = () => {
// }
debugger
function App() {
console.log('%c让我康康 App Render', 'border-radius:3px;background:#000;color:#f6e387;padding: 2px 5px', );
const [name, setName] = React.useState('Jack');
const [number, setNumber] = React.useState(0);
let memoData = useMemo(() => ({number}),[number]);
let onClick = useCallback(() => setNumber(number + 1),[number] );
return (
<div className="App">
<h1>{name}</h1>
<input type="text" onChange={(e)=>{setName(e.target.value)} } />
<Child data={memoData} handleClick={onClick} />
</div>
)
}
function render() {
ReactDOM.render(
<App />,
document.getElementById('root')
)
}
render();
1.userContext的使用
使用场景, 组件之间需要共享状态
父组件
import React, { useState } from 'react';
import Child from './child.js'
import Child2 from './child2.js'
import CountContext from './common'
function Example(){
const [count , setCount ] = useState(666)
return(
<div>
<div>
<p>you click {count -666 } times</p>
<button onClick={()=>{setCount(count+1)}}>click me</button>
<CountContext.Provider value ={count}>
<Child/>
<Child2/>
</CountContext.Provider>
</div>
</div>
)
}
export default Example
子组件1
import React, { useContext } from 'react';
import CountContext from './common'
function Child(){
let count = useContext(CountContext)
return(<h2>儿子1:{count}</h2>)
}
export default Child
子组件2
import React, { useContext } from 'react';
import CountContext from './common'
function Child(){
let count = useContext(CountContext)
return(<h2>儿子2:{count}</h2>)
}
export default Child
公共组件
import {createContext } from 'react';
const CountContext = createContext(80);
export default CountContext
效果
二、父子传值
父组件
<ConfirmModal
attribution={attribution}
location={props.location}
func={confirmClose}
onFinish={() => {
confirmClose();
setFinish(true);
}}
agent_id={
props.location.query.agent_id
? props.location.query.agent_id
: ''
}
values={values}
number={number}
products={screen.products}
/>
子组件
export default ({location, values, products, number = { price: 0 }, func, onFinish, attribution, agent_id}) => {
//触发父组件函数
//写入默认值 相当于 this.setState
useState(() => {
//写入邮箱默认值
if (values.email === undefined || values.email === '') {
values.email = values.select_number + '@189.cn';
}
//写入宽带安装地址默认值
if (values.broadband_address && values.broadband_address.length > 0) {
values.broadband_address = attribution + values.broadband_address;
}
//写入承保地址默认值
if (
values.gift_address === undefined ||
(values.gift_address === '' &&
product.gifts.find((item) => item.id === values.gift_id)
.need_address)
) {
values.gift_address = values.detail_address;
}
});
onFinish && onFinish(); //可传值
}
3.卸载时清除定时器
const time = React.useRef(null) useEffect(() => { return() => { clearIntervel(time.current) } });
4.根据某个值的修改 触发其他函数
//只要value值变化了 , 就会触发getList()函数
useEffect(() => { getList() }, [value]);
使用useCallback和useMemo时, 要注意函数形式
如果是函数形式, 参数变成行参了, 就不需要在依赖项加上变量了
useCallback(() => setOpen(!open), [open]);
// 可以写成
useCallback(() => setOpen((open) => !open), []);
如果写成函数, 会被依赖项忽略掉
const funcB = useCallback(() => { setOpen(!open); }, [open]); useEffect(() => { console.log('funcB :>> ', funcB); }, [funcB]);
// =>
5. 卸载React应用
import { unmountComponentAtNode } from 'react-dom'; const domNode = document.getElementById('root'); render(<App />, domNode); unmountComponentAtNode(domNode);
// React 18root.unmount()
.
遇到的BUG
1.使用create-react-app 创建应用的时候
指令create-react-app 不存在 ,使用npx create-react-app 即可
2.使用antd-design 组件Button时 报错
经检查,发现是antd版本没跟上react更新导致
解决方法 降低react和react-dom 版本为16.12.0 , antd版本降为4.4.2