You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
(1)不像 class 中的 this.setState ,Hook 更新 state 变量总是替换它而不是合并它;
(2)推荐使用多个 state 变量,而不是单个 state 变量,因为 state 的替换逻辑而不是合并逻辑,并且利于后续的相关 state 逻辑抽离;
(3)调用 State Hook 的更新函数并传入当前的 state 时,React 将跳过子组件的渲染及 effect 的执行。(React 使用 Object.is 比较算法 来比较 state。)
二、Effect Hook
1、基础用法
functionEffect(){const[count,setCount]=useState(0);useEffect(()=>{console.log(`You clicked ${count} times`);});return(<div><p>You clicked {count} times</p><buttononClick={()=>setCount(count+1)}>
Click me
</button></div>)}
importReact,{useState}from'react';importThemeContextfrom'./ThemeContext';importContextComponent1from'./ContextComponent1';functionContextPage(){const[count,setCount]=useState(1);return(<divclassName="App"><ThemeContext.Providervalue={count}><ContextComponent1/></ThemeContext.Provider><buttononClick={()=>setCount(count+1)}>
Click me
</button></div>);}exportdefaultContextPage;
如下所示,当父组件重新渲染时,子组件也会重新渲染,即使子组件的 props 和 state 都没有改变
importReact,{memo,useState}from'react';// 子组件constChildComp=()=>{console.log('ChildComp...');return(<div>ChildComp...</div>);};// 父组件constParent=()=>{const[count,setCount]=useState(0);return(<divclassName="App"><div>hello world {count}</div><divonClick={()=>{setCount(count=>count+1);}}>点击增加</div><ChildComp/></div>);};exportdefaultParent;
假设以下场景,父组件在调用子组件时传递 info 对象属性,点击父组件按钮时,发现控制台会打印出子组件被渲染的信息。
importReact,{memo,useState}from'react';// 子组件constChildComp=(info:{info:{name: string,age: number}})=>{console.log('ChildComp...');return(<div>ChildComp...</div>);};constMemoChildComp=memo(ChildComp);// 父组件constParent=()=>{const[count,setCount]=useState(0);const[name]=useState('jack');const[age]=useState(11);constinfo={ name, age };return(<divclassName="App"><div>hello world {count}</div><divonClick={()=>{setCount(count=>count+1);}}>点击增加</div><MemoChildCompinfo={info}/></div>);};exportdefaultParent;
分析原因:
点击父组件按钮,触发父组件重新渲染;
父组件渲染,const info = { name, age } 一行会重新生成一个新对象,导致传递给子组件的 info 属性值变化,进而导致子组件重新渲染。
解决:
使用 useMemo 将对象属性包一层,useMemo 有两个参数:
第一个参数是个函数,返回的对象指向同一个引用,不会创建新对象;
第二个参数是个数组,只有数组中的变量改变时,第一个参数的函数才会返回一个新的对象。
importReact,{memo,useMemo,useState}from'react';// 子组件constChildComp=(info:{info:{name: string,age: number}})=>{console.log('ChildComp...');return(<div>ChildComp...</div>);};constMemoChildComp=memo(ChildComp);// 父组件constParent=()=>{const[count,setCount]=useState(0);const[name]=useState('jack');const[age]=useState(11);// 使用 useMemo 将对象属性包一层constinfo=useMemo(()=>({ name, age }),[name,age]);return(<divclassName="App"><div>hello world {count}</div><divonClick={()=>{setCount(count=>count+1);}}>点击增加</div><MemoChildCompinfo={info}/></div>);};exportdefaultParent;
importReact,{memo,useMemo,useState}from'react';// 子组件constChildComp=(props:any)=>{console.log('ChildComp...');return(<div>ChildComp...</div>);};constMemoChildComp=memo(ChildComp);// 父组件constParent=()=>{const[count,setCount]=useState(0);const[name]=useState('jack');const[age]=useState(11);constinfo=useMemo(()=>({ name, age }),[name,age]);constchangeName=()=>{console.log('输出名称...');};return(<divclassName="App"><div>hello world {count}</div><divonClick={()=>{setCount(count=>count+1);}}>点击增加</div><MemoChildCompinfo={info}changeName={changeName}/></div>);};exportdefaultParent;
分析下原因:
点击父组件按钮,改变了父组件中 count 变量值(父组件的 state 值),进而导致父组件重新渲染;
importReact,{memo,useCallback,useMemo,useState}from'react';// 子组件constChildComp=(props:any)=>{console.log('ChildComp...');return(<div>ChildComp...</div>);};constMemoChildComp=memo(ChildComp);// 父组件constParent=()=>{const[count,setCount]=useState(0);const[name]=useState('jack');const[age]=useState(11);constinfo=useMemo(()=>({ name, age }),[name,age]);constchangeName=useCallback(()=>{console.log('输出名称...');},[]);return(<divclassName="App"><div>hello world {count}</div><divonClick={()=>{setCount(count=>count+1);}}>点击增加</div><MemoChildCompinfo={info}changeName={changeName}/></div>);};exportdefaultParent;
前言
React 在 v16.8 的版本中推出了 React Hooks 新特性。在我看来,使用 React Hooks 相比于从前的类组件有以下几点好处:
关于这方面的文章,我们根据使用场景分别进行举例说明,帮助你认识理解并可以熟练运用 React Hooks 大部分特性。
辛苦整理良久,还望手动点赞鼓励~
博客 github地址为:https://github.com/fengshi123/blog ,汇总了作者的所有博客,欢迎关注及 star ~
一、State Hook
1、基础用法
2、更新
更新分为以下两种方式,即直接更新和函数式更新,其应用场景的区分点在于:
3、实现合并
与 class 组件中的 setState 方法不同,useState 不会自动合并更新对象,而是直接替换它。我们可以用函数式的 setState 结合展开运算符来达到合并更新对象的效果。
4、惰性初始化 state
initialState 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。其应用场景在于:创建初始 state 很昂贵时,例如需要通过复杂计算获得;那么则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用:
5、一些重点
(1)不像 class 中的 this.setState ,Hook 更新 state 变量总是替换它而不是合并它;
(2)推荐使用多个 state 变量,而不是单个 state 变量,因为 state 的替换逻辑而不是合并逻辑,并且利于后续的相关 state 逻辑抽离;
(3)调用 State Hook 的更新函数并传入当前的 state 时,React 将跳过子组件的渲染及 effect 的执行。(React 使用 Object.is 比较算法 来比较 state。)
二、Effect Hook
1、基础用法
2、清除操作
为防止内存泄漏,清除函数会在组件卸载前执行;如果组件多次渲染(通常如此),则在执行下一个 effect 之前,上一个 effect 就已被清除,即先执行上一个 effect 中 return 的函数,然后再执行本 effect 中非 return 的函数。
3、执行时期
与 componentDidMount 或 componentDidUpdate 不同,使用 useEffect 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快;(componentDidMount 或 componentDidUpdate 会阻塞浏览器更新屏幕)
4、性能优化
默认情况下,React 会每次等待浏览器完成画面渲染之后延迟调用 effect;但是如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可:如下所示,如果 count 值两次渲染之间没有发生变化,那么第二次渲染后就会跳过 effect 的调用;
5、模拟 componentDidMount
如果想只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([ ])作为第二个参数,如下所示,原理跟第 4 点性能优化讲述的一样;
6、最佳实践
要记住 effect 外部的函数使用了哪些 props 和 state 很难,这也是为什么 通常你会想要在 effect 内部 去声明它所需要的函数。
如果处于某些原因你无法把一个函数移动到 effect 内部,还有一些其他办法:
推荐启用 eslint-plugin-react-hooks 中的 exhaustive-deps 规则,此规则会在添加错误依赖时发出警告并给出修复建议 ;
7、一些重点
(1)可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate和 componentWillUnmount这三个函数的组合;
(2)在 React 的 class 组件中,render 函数是不应该有任何副作用的; 一般来说,在这里执行操作太早了,我们基本上都希望在 React 更新 DOM 之后才执行我们的操作。
三、useContext
用来处理多层级传递数据的方式,在以前组件树中,跨层级祖先组件想要给孙子组件传递数据的时候,除了一层层 props 往下透传之外,我们还可以使用 React Context API 来帮我们做这件事。使用例子如下所示
(1)使用 React Context API,在组件外部建立一个 Context
(2)使用 Context.Provider提供了一个 Context 对象,这个对象可以被子组件共享
(3)useContext()钩子函数用来引入 Context 对象,并且获取到它的值
四、useReducer
1、基础用法
比 useState 更适用的场景:例如 state 逻辑处理较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等;例子如下所示
2、惰性初始化 state
五、Memo
如下所示,当父组件重新渲染时,子组件也会重新渲染,即使子组件的 props 和 state 都没有改变
改进:我们可以使用 memo 包一层,就能解决上面的问题;但是仅仅解决父组件没有传参给子组件的情况以及父组件传简单类型的参数给子组件的情况(例如 string、number、boolean等);如果有传复杂属性应该使用 useCallback(回调事件)或者 useMemo(复杂属性)
六、useMemo
假设以下场景,父组件在调用子组件时传递 info 对象属性,点击父组件按钮时,发现控制台会打印出子组件被渲染的信息。
分析原因:
解决:
使用 useMemo 将对象属性包一层,useMemo 有两个参数:
七 、useCallback
接着第六章节的例子,假设需要将事件传给子组件,如下所示,当点击父组件按钮时,发现控制台会打印出子组件被渲染的信息,说明子组件又被重新渲染了。
分析下原因:
解决:
修改父组件的 changeName 方法,用 useCallback 钩子函数包裹一层, useCallback 参数与 useMemo 类似
八、useRef
以下分别介绍 useRef 的两个使用场景:
1、指向 dom 元素
如下所示,使用 useRef 创建的变量指向一个 input 元素,并在页面渲染后使 input 聚焦
2、存放变量
useRef 在 react hook 中的作用, 正如官网说的, 它像一个变量, 类似于 this , 它就像一个盒子, 你可以存放任何东西. createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用,如下例子所示:
九、useImperativeHandle
使用场景:通过 ref 获取到的是整个 dom 节点,通过 useImperativeHandle 可以控制只暴露一部分方法和属性,而不是整个 dom 节点。
十、useLayoutEffect
其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect,这里不再举例。
总结
关于这方面的文章,我们根据使用场景分别进行举例说明,希望有帮助到你认识理解并可以熟练运用 React Hooks 大部分特性。
辛苦整理良久,还望手动点赞鼓励~
博客 github地址为:https://github.com/fengshi123/blog ,汇总了作者的所有博客,欢迎关注及 star ~
The text was updated successfully, but these errors were encountered: