经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » JavaScript » 查看文章
React Hook用法详解(6个常见hook)
来源:cnblogs  作者:是明啊  时间:2021/5/6 17:56:53  对本文有异议

1、useState:让函数式组件拥有状态

用法示例:

  1. // 计数器
  2. import { useState } from 'react'
  3. const Test = () => {
  4. const [count, setCount] = useState(0);
  5. return (
  6. <>
  7. <h1>点击了{count}次</h1>
  8. <button onClick={() => setCount(count + 1)}>+1</button>
  9. </>
  10. );
  11. }
  12. export default Test

PS:class组件中this.setState更新是state是合并, useState中setState是替换。例如:

  1. // 错误示例
  2. import { useState } from 'react'
  3. const Test = () => {
  4. const [counts, setCounts] = useState({
  5. num1: 0,
  6. num2: 0
  7. });
  8. return (
  9. <>
  10. <h1>num1:{counts.num1}</h1>
  11. <h1>num2:{counts.num2}</h1>
  12. <button onClick={() => setCounts({ num1: counts.num1 + 1})}>num1+1</button>
  13. <button onClick={() => setCounts({ num2: counts.num2 + 1})}>num2+1</button>
  14. </>
  15. );
  16. }
  17. export default Test


可以看到useState中setState是替换,不会合并,正确更新:

  1. import { useState } from 'react'
  2. const Test = () => {
  3. const [counts, setCounts] = useState({
  4. num1: 0,
  5. num2: 0
  6. });
  7. return (
  8. <>
  9. <h1>num1:{counts.num1}</h1>
  10. <h1>num2:{counts.num2}</h1>
  11. <button onClick={() => setCounts({ ...counts, num1: counts.num1 + 1})}>num1+1</button>
  12. <button onClick={() => setCounts({ ...counts, num2: counts.num2 + 1})}>num2+1</button>
  13. </>
  14. );
  15. }
  16. export default Test

2、useEffect:副作用,取代生命周期

用法示例,在class组件中如果需要在组件挂载后和数据更新后做同一件事,我们会这样做:

  1. componentDidMount() {
  2. // 做一些事
  3. }
  4. componentDidUpdate() {
  5. // 做一些事
  6. }

可以看出来,如果逻辑复杂后,代码看起来不优雅,且容易造成逻辑混乱,而使用useEffect:

  1. useEffect(() => {
  2. // 做一些事
  3. });

此刻已经看到了useEffect的基本用法,除此之外,他还可以绑定触发更新的依赖状态,默认是状态中任何数据发生变化副作用都会执行,如:

  1. import { useState, useEffect } from 'react'
  2. const Test = () => {
  3. const [count1, setCount1] = useState(0);
  4. const [count2, setCount2] = useState(0);
  5. useEffect(() => {
  6. console.log('useEffect触发了')
  7. });
  8. return (
  9. <>
  10. <h1>count1:{count1}</h1>
  11. <h1>count2:{count2}</h1>
  12. <button onClick={() => setCount1(count1 + 1)}>count1+1</button>
  13. <button onClick={() => setCount2(count2 + 1)}>count2+1</button>
  14. </>
  15. );
  16. }
  17. export default Test


将上述代码useEffect第二个参数传入需要绑定的状态,可绑定多个:

  1. // 语法:useEffect(回调函数,[依赖值])
  2. useEffect(() => {
  3. console.log('useEffect触发了')
  4. }, [count1]);


可以看到,只有绑定的count1发生变化才会触发,如果传空数组则任何状态发生变化都不会触发,此时useEffect的作用就类似class组件中的componentDidMount,所以发送请求通常也会在此执行。

清理副作用
在上面的操作中都不用清理的副作用,然而,有些副作用是需要去清理的,不清理会造成异常甚至内存泄漏,比如开启定时器,如果不清理,则会多次开启,从上面可以看到useEffect的第一个参数是一个回调函数,可以在回调函数中再返回一个函数,该函数可以在状态更新后第一个回调函数执行之前调用,具体实现:

  1. useEffect(() => {
  2. // 设置副作用
  3. return () => {
  4. // 清理副作用
  5. }
  6. });

3、useContext:跨组件共享数据

React.createContext();创建一个TestContext对象
TestContext.Provider包裹子组件
数据放在<TestContext.Provider value={value}>的value中
子组件中通过useContext(TestContext)获取值

  1. import React, { useContext, useState } from 'react';
  2. const TestContext = React.createContext();
  3. const Parent = () => {
  4. const [value, setValue] = useState(0);
  5. return (
  6. <div>
  7. {(() => console.log("Parent-render"))()}
  8. <button onClick={() => setValue(value + 1)}>value + 1</button>
  9. <TestContext.Provider value={value}>
  10. <Child1 />
  11. <Child2 />
  12. </TestContext.Provider>
  13. </div>
  14. );
  15. }
  16. const Child1 = () => {
  17. const value = useContext(TestContext);
  18. return (
  19. <div>
  20. {(() => console.log('Child1-render'))()}
  21. <h3>Child1-value: {value}</h3>
  22. </div>
  23. );
  24. }
  25. const Child2 = () => {
  26. return (
  27. <div>
  28. {(() => console.log('Child2-render'))()}
  29. <h3>Child2</h3>
  30. </div>
  31. );
  32. }
  33. export default Parent


至此数据实现共享了,但是可以看到在TestContext中的共享数据只要发生变化,子组件都会重新渲染,Child2并没有绑定数据,不希望他做无意义的渲染,可以使用React.memo解决,实现:

  1. const Child2 = React.memo(() => {
  2. return (
  3. <div>
  4. {(() => console.log('Child2-render'))()}
  5. <h3>Child2</h3>
  6. </div>
  7. );
  8. });

4、useCallback:性能优化

语法:

  1. // useCallback(回调函数,[依赖值])
  2. const handleClick = useCallback(()=> {
  3. // 做一些事
  4. }, [value]);

useCallback返回的是一个 memoized(缓存)函数,在依赖不变的情况下,多次定义的时候,返回的值是相同的,他的实现原理是当使用一组参数初次调用函数时,会缓存参数和计算结果,当再次使用相同的参数调用该函数时,会直接返回相应的缓存结果。

优化性能例子:

  1. import React, { useState, useCallback, memo } from 'react';
  2. const Parent = () => {
  3. const [value1, setValue1] = useState(0);
  4. const [value2, setValue2] = useState(0);
  5. const handleClick1 = useCallback(()=> {
  6. setValue1(value1 + 1);
  7. }, [value1]);
  8. const handleClick2 = useCallback(()=> {
  9. setValue2(value2 + 1);
  10. }, [value2]);
  11. return (
  12. <>
  13. {(() => console.log("Parent-render"))()}
  14. <h3>{value1}</h3>
  15. <h3>{value2}</h3>
  16. <Child1 handleClick1={handleClick1} />
  17. <Child2 handleClick2={handleClick2} />
  18. </>
  19. );
  20. }
  21. const Child1 = memo(props => {
  22. return (
  23. <div>
  24. {(() => console.log("Child1-render"))()}
  25. <button onClick={() => props.handleClick1()}>value1 + 1</button>
  26. </div>
  27. );
  28. });
  29. const Child2 = memo(props => {
  30. return (
  31. <div>
  32. {(() => console.log("Child2-render"))()}
  33. <button onClick={() => props.handleClick2()}>value2 + 1</button>
  34. </div>
  35. );
  36. });
  37. export default Parent

useCallback返回的是一个memoized回调函数,仅在其中绑定的一个依赖项变化后才更改可防止不必要的渲染,在跨组件共享数据中举例的事件是在父组件中点击触发,而现在是使用状态提升,在父组件中传递方法供子组件调用,每次render时函数也会变化,导致子组件重新渲染,上面例子useCallback将函数进行包裹,依赖值未发生变化时会返回缓存的函数,配合React.memo即可优化无意义的渲染。

5、useMemo:性能优化

语法:

  1. // useMemo(回调函数,[依赖值])
  2. useMemo(() => {
  3. // 做一些事情
  4. },[value]);

先看一个例子:

  1. import React, { useState } from 'react'
  2. const Test = ()=> {
  3. const [value, setValue] = useState(0);
  4. const [count, setCount] = useState(1);
  5. const getDoubleCount = () => {
  6. console.log('getDoubleCount进行计算了');
  7. return count * 2;
  8. };
  9. return (
  10. <div>
  11. <h2>value: {value}</h2>
  12. <h2>doubleCount: {getDoubleCount()}</h2>
  13. <button onClick={() => setValue(value + 1)}>value+1</button>
  14. </div>
  15. )
  16. }
  17. export default Test

可以看到getDoubleCount依赖的是count,但value发生变化它也重新进行了计算渲染,现在只需要将getDoubleCount使用useMemo进行包裹,如下:

  1. import React, { useState, useMemo } from 'react'
  2. const Test = ()=> {
  3. const [value, setValue] = useState(0);
  4. const [count, setCount] = useState(1);
  5. const getDoubleCount = useMemo(() => {
  6. console.log('getDoubleCount进行计算了');
  7. return count * 2;
  8. },[count]);
  9. return (
  10. <div>
  11. <h2>value: {value}</h2>
  12. <h2>doubleCount: {getDoubleCount}</h2>
  13. <button onClick={() => setValue(value + 1)}>value+1</button>
  14. </div>
  15. )
  16. }
  17. export default Test


现在getDoubleCount只有依赖的count发生变化时才会重新计算渲染。

useMemo和useCallback的共同点:

  • 接收的参数都是一样的,第一个是回调函数,第二个是依赖的数据
  • 它们都是当依赖的数据发生变化时才会重新计算结果,起到了缓存作用

useMemo和useCallback的区别:

  • useMemo计算结果是return回来的值,通常用于缓存计算结果的值
  • useCallback计算结果是一个函数,通常用于缓存函数

6、useRef

用法:例如要实现点击button按钮使input输入框获得焦点:
  1. import React, { useRef } from 'react';
  2. const Test = () => {
  3. const inputEl = useRef();
  4. return (
  5. <>
  6. <input ref={inputEl} />
  7. <button onClick={() => inputEl.current.focus()}>focus</button>
  8. </>
  9. );
  10. }
  11. export default Test

这样看起来非常像React.createRef(),将上面代码中的useRef()改成React.createRef()也能实现同样的效果,那为什么要设计一个新的hook?难道只是会了加上use,统一hook规范?
事实上,它们确实不一样。

官网的说明如下:

  1. useRef returns a mutable ref object whose .current property is initialized to the passed
  2. argument (initialValue). The returned object will persist for the full lifetime of the component.

翻译:

简单来说,useRef就像一个储物箱,你可以随意存放任何东西,再次渲染时它会去储物箱找,createRef每次渲染都会返回一个新的引用,而useRef每次都会返回相同的引用。

原文链接:http://www.cnblogs.com/hymenhan/p/14711516.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号