参考文章 react源码, useEffect 完整指南, 呕心沥血,一文看懂 react hooks, react官网
在进入正式阅读以前,最好先思考一下下面的问题:html
我很是喜欢参考文章里面说的,若是你想要学好React Hooks,那么你摒弃掉以前组件的想法可能会更加好
为何我会这么讲?前端
React Hooks会璞归真,其实就是将咱们以前封装好的组件对象从新变回来了咱们原始的代码模式。让你只要考虑执行过程当中的栈,堆和队列react
在React Hooks里面,当咱们声明一个组件时缓存
import React from 'react'; function App() { return ( <div className="App"> 我是React Hooks </div> ); } export default App;
咱们能够很明显的看到,这就是一个函数嘛,只是加入JSX的写法,返回了组件而已。是的,你没有理解错,这应该也是React Hooks的创始人的想法,不比Vue,虽然轻量,可是封装好了一切,致使可能前端就是学习框架去了~闭包
可是,React不也是组件化的框架吗?是的,当代码增多框架
import React, { useState, useEffect } from 'react'; function App() { const [name, setName] = useState('hello'); useEffect(() => { console.log(name) }) return ( <div className="App" onClick={() => {setName(name + 'world')}}> 我是{name} </div> ); } export default App;
让咱们来大胆的猜测一下,当这段代码运行完了以后,页面会发生什么?less
// 第一次渲染 -----函数开始----- useState定义了一个为'name'的state useEffect定义了一个函数,他没有任何的依赖项 返回一个JSX显示到咱们页面上 页面加载成功,发现咱们有个useEffect,发现他不依赖任何属性,他就要运行,因而他今后次函数中拿到name-->打印hello -----函数结束----- // 当点击咱们的文本 点击事件回调修改咱们的state,告诉咱们相应的组件,你须要更新了 -----函数开始----- 从咱们的state中拿出修改后的'name',这时候name为helloworld 第二次渲染了,useEffect已经被声明过了,不理他了 返回一个JSX显示到咱们页面上 页面加载成功,发现咱们有个useEffect,发现他不依赖任何属性,他就要运行,因而他今后次函数中拿到name-->打印helloworld -----函数结束-----
想必,看完了这段例子以后,你对React Hooks也有了必定的理解了。其实就是使用useState去存储咱们须要存储的数据,当他更新的时候刷新页面,当页面刷新的时候,咱们再使用useEffect来进行咱们须要的操做。ide
这样看的话,useEffect岂不就是至关于咱们以前的componentDidMount + componentDidUpdate函数
我能够很负责任的告诉你,不是的,useEffect是咱们更新页面的反作用,当咱们对他加上了依赖项以后,他就会在页面加载完了以后,检查依赖项是否有变化来进行决定是否要运行本身,例如:组件化
import React, { useState, useEffect } from 'react'; function App() { const [name, setName] = useState('hello'); const [myName, setMyName] = useState('my Hello') useEffect(() => { console.log('我是第一个反作用' + name) }) useEffect(() => { console.log('我是第二个反作用' + name) }, []) useEffect(() => { console.log('我是第三个反作用' + name) }, [myName]) useEffect(() => { console.log('我是第四个反作用' + name) }, [name]) return ( <div className="App" onClick={() => {setName(name + 'world')}}> 我是{name} </div> ); } export default App;
这时候咱们执行完了以后和点击文本以后会发生什么呢?
// 第一次渲染 -----函数开始----- ...(省略相同步骤) 页面加载完毕 发现没有依赖项,打印:我是第一个反作用hello 发现是首次渲染,打印:我是第二个反作用hello 发现是首次渲染,打印:我是第三个反作用hello 发现是首次渲染,打印:我是第四个反作用hello -----函数开始----- // 点击咱们的文本 更新state -----函数开始----- ...(省略相同步骤) 发现没有依赖项,打印:我是第一个反作用helloworld 发现依赖项仍是空,与上次相同,不打印 发现myName没有更新,与上次相同,不打印 发现name更新,打印:我是第四个反作用helloworld -----函数结束-----
详情可参考例子:https://codesandbox.io/s/reac...
上面的例子可能还没可以理解为何是函数式渲染,接下来这一次你可能就能意会到,并且是开发中可能常常出现的问题,看下面的例子
function Counter() { const [count, setCount] = useState(0); function handleAlertClick() { setTimeout(() => { alert('You clicked on: ' + count); }, 3000); } return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> <button onClick={handleAlertClick}> Show alert </button> </div> ); }
在该例子中,咱们点击了Show alert以后,再去点击Click me,发现弹出来的是了Show alert时候的count,详情可参考例子:https://codesandbox.io/s/pric...
为何会出现这样的状况呢
由于Counter是一个函数,在点击Show alert,此时的count为当前值,在咱们每次点击Click me的时候Counter函数都会运行一次。这样是否是就能理解了呢
// 🔴 在条件语句中使用 Hook 违反第一条规则 if (name !== '') { useEffect(function persistForm() { localStorage.setItem('formData', name); }); } // 第二次调用 useState('Mary') // 1. 读取变量名为 name 的 state(参数被忽略) // useEffect(persistForm) // 🔴 此 Hook 被忽略! useState('Poppins') // 🔴 2 (以前为 3)。读取变量名为 surname 的 state 失败 useEffect(updateTitle) // 🔴 3 (以前为 4)。替换更新标题的 effect 失败