赞
踩
React Hook
新出现背景providers customers
,等一堆工具都是为了解决这个问题,但是造成了很严重的理解成本和组件嵌套地狱state
和 生命周期,导致使用场景有限React Hook
Hooks
是 React 16.8
新增的特性,它可以让你在不编写 class
的情况下使用 state
以及其他的 React
特性,无需转化成类组件
Hook
的使用和实践useState
和 Hook
的闭包机制// hook 组件 function Counter() { const [count, setCount] = useState(0); const log = () => { setCount(count + 1); setTimeout(() => { console.log(count); }, 3000); }; return ( <div> <p>You clicked { count} times</p> <button onClick={ log}>Click me</button> </div> ); } // 等效的类组件 class Counter extends Component { state = { count: 0 }; log = () => { this.setState({ count: this.state.count + 1, }); setTimeout(() => { console.log(this.state.count); }, 3000); }; render() { return ( <div> <p>You clicked { this.state.count} times</p> <button onClick={ this.log}>Click me</button> </div> ); } }
快速点击下的情况下,想想 Hook
组件和函数式组件控制台打印出来的是什么?
3 3 3
Class
组件的 state
是不可变的,通过 setState
返回一个新的引用,this.state
指向一个新的引用setTimeout
执行的时候,通过 this
获取最新的 state
引用,所以这个输出都是 3
0 1 2
props
和 state
既然每次渲染都是一个独立的闭包,可以尝试代码拆解函数式组件的渲染过程
// 第一次点击 function Counter() { const [0, setCount] = useState(0); const log = () => { setCount(0 + 1); // 只能获取这次点击按钮的 state setTimeout(() => { console.log(0); }, 3000); }; } // 第二次点击 function Counter() { const [1, setCount] = useState(0); const log = () => { setCount(1 + 1); setTimeout(() => { console.log(1); }, 3000); }; } // 第三次点击 function Counter() { const [2, setCount] = useState(0); const log = () => { setCount(2 + 1); setTimeout(() => { console.log(2); }, 3000); }; }
三次点击,共 4
次渲染,count
从 0
变为 3
页面第一次渲染,页面看到的 count = 0
第一次点击,事件处理器获取的 count = 0
,count
变成 1
, 第二次渲染,渲染后页面看到 count = 1
,对应上述代码第一次点击
第二次点击,事件处理器获取的 count = 1
,count
变成 2
, 第三次渲染,渲染后页面看到 count = 2
,对应上述代码第二次点击
第三次点击,事件处理器获取的 count = 2
,count
变成 3
, 第四次渲染,渲染后页面看到 count = 3
,对应上述代码第三次点击
3 3 3
有种比较简单并且能解决问题的方案,借用 useRef
useRef
返回一个可变的 ref
对象,其 current
属性被初始化为传入的参数(initialValue)
useRef
返回的 ref
对象在组件的整个生命周期内保持不变,也就是说每次重新渲染函数组件时,返回的 ref
对象都是同一个useRef
可以类比成类组件实例化后的 this
,在组件没有销毁的返回的引用都是同一个function Counter() { const count = useRef(0); const log = () => { count.current++; setTimeout(() => { console.log(count.current); }, 3000); }; return ( <div> <p>You clicked { count.current} times</p> <button onClick={ log}>Click me</button> </div> ); }
3 3 3
Ref
对象整个生命周期都不变,修改 current
属性也只是修改属性,那除了打印,这里的 You clicked 0 times
,点击三次,会变成 3
么?3
次,但是不会像 useState
一样,渲染 4
次,这里只会渲染 1
次,然后看到的都是 You clicked 0 times
通过 useRef
虽然能解决打印的问题,但是页面渲染是不对的,这里还是使用 useState
的方案,配合 useEffect
可以实现我们想要的效果
function useEffect(effect: EffectCallback, deps?: DependencyList): void;
useEffect
的签名,effect
是函数类型,并且必填, 还有第二个可选参数,类型是只读数组useEffect
是处理副作用的,其执行时机在 每次 Render
渲染完毕后,换句话说就是每次渲染都会执行,在真实 DOM
操作完毕后。配合这个 hook
, 如果每次 state
改变后渲染完之后,把 ref
里面的值更新,然后控制台打印 ref
的值,参考React实战视频讲解:进入学习
function Counter() { const [count, setCount] = useState(0); const currentCount = useRef(count); useEffect(() => { currentCount.current = count; }); const log = () => { setCount(count + 1); setTimeout(() => { console.log(currentCount.current); }, 3000); }; return ( <div> <p>You clicked { count} times
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。