React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)

文章目录

  • 1. React Hooks
    • 1.1 React Hooks 优势
    • 1.2 React 常用 Hook
    • 1.3 Hook 使用规则
    • 1.4 自定义 Hook
  • 2. useMemo
    • 2.1 example07-1
      • 2.1.1 example07-2
        • 2.1.1.1 useMemo与useEffect区别
      • 2.1.2 example07-3
  • 3. useCallback
    • 3.1 example08
    • 3.2 [useMemo和useCallback的区别 及使用场景]
  • 4. useContext
    • 4.1 example09
      • 4.1.1 example09-1
      • 4.1.2 example09-2
      • 4.1.3 example09-3
      • 4.1.4 example09-4
      • 4.1.5 example09-5
  • 5. useReducer
    • 5.1 example10
      • 5.1.1 example10-1
      • 5.1.2 example10-2
  • 6. 自定义hook
    • 6.1 example11
    • 6.2 Hook 使用规则

1. React Hooks

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

1.1 React Hooks 优势

  • 简化组件逻辑
  • 复用状态逻辑
  • 无需使用类组件编写

1.2 React 常用 Hook

注意:所有的Hooks都是用use来声明的。

  • useState
    const [state, setState] = useState(initialState);
  • useEffect
    类组件
    componentDidMount、componentDidUpdate 和 componentWillUnmount
    需要清除的副作用
  • useRef
  • useContext
    context
    createContext、Provider、Consumer、contextType
  • useMemo
  • useCallback

1.3 Hook 使用规则

  • 只在最顶层使用 Hook
  • 只在 React 函数中调用 Hook
    • React 函数组件中
    • React Hook 中

1.4 自定义 Hook

通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。

考虑到在blog中不好体现代码更改的位置,小迪才用github托管代码,大家可以查看github,看到详细版本修改过程,搭配博客学习。

注意:上一篇已经详述了useState、useEffect、useRef,现在总结后面的内容了。

2. useMemo

2.1 example07-1

code\project\study-hooks\react-app\src\hooks\memo.js

import React, { useState, useMemo} from 'react';
 
function Memo() {
    const [name,setName] = useState("leo");
    const [age,setAge] = useState(10);
    let age2 = (()=>{
        console.log(1);
        return age < 18?"未成年":"成年人";
    })();
    return (<div>
        姓名:{name},<br />
        年龄:{age},<br />
        年龄阶段:{age2},<br />
        <button onClick={()=>{
            setAge(age+2);
        }}>长大</button>
    </div>);
}
 
export default Memo;
 

存在问题:当并不需要修改年龄阶段的时候,也会调用该函数。每次组件更新,都会执行该函数。而我们只需要某些时候,即age大于18之后,才需要调用函数。

这个时候就用到了useMemo,它的作用就是优化计算性能的,如某些函数仅仅依赖某些状态的改变,才去执行,这个时候就可以把它放入useMemo中了。

只有某些状态发生了变化,才能执行useMemo

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第1张图片

参考:https://github.com/6xiaoDi/Projec-React-hooks-enterprise-level-mbapp/tree/as0.07-1
Branch: bhStudy-Hooks

commit description:as0.07-1-example07-1(useMemo ——引例)

tag:as0.07-1

2.1.1 example07-2

useMemo的写法和useEffect写法非常像,接收的也是一个函数,第二个参数也是这个函数执行需要依赖于谁,即谁改变了才会执行该函数。

有人疑问为啥不用useEffect实现呢?

import React, {useState, useMemo, useEffect} from 'react';
 
function Memo() {
    const [name,setName] = useState("leo");
    const [age,setAge] = useState(10);
    let age2 = useMemo(()=>{
        console.log(1);
        return age < 18?"未成年":"成年人";
    },[]);
 
    useEffect(()=>{
        console.log("更新完成之后!")
    },[])
 
    return (<div>
        姓名:{name},<br />
        年龄:{age},<br />
        年龄阶段:{age2},<br />
        <button onClick={()=>{
            setAge(age+2);
        }}>长大</button>
    </div>);
}
 
export default Memo;
 

因为执行阶段不一样,useEffect副作用钩子是当我们组件更新完成之后才执行了,而useMemo是更新前执行。

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第2张图片

import React, {useState, useMemo, useEffect} from 'react';
 
function Memo() {
    const [name,setName] = useState("leo");
    const [age,setAge] = useState(10);
    let age2 = useMemo(()=>{
        console.log(1);
        return age < 18?"未成年":"成年人";
    },[age]);
 
    useEffect(()=>{
        console.log("更新完成之后!")
    },[age])
    console.log("开始更新了");
    return (<div>
        姓名:{name},<br />
        年龄:{age},<br />
        年龄阶段:{age2},<br />
        <button onClick={()=>{
            setAge(age+2);
        }}>长大</button>
    </div>);
}
 
export default Memo;
 

这就能完整看到整个阶段更新前、更新开始、更新后了。

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第3张图片

参考:https://github.com/6xiaoDi/Projec-React-hooks-enterprise-level-mbapp/tree/as0.07-2
Branch: bhStudy-Hooks

commit description:as0.07-2-example07-2(useMemo ——其与useEffect区别)

tag:as0.07-2

2.1.1.1 useMemo与useEffect区别

useMemo会在视图更新之前,就调用了,而在useEffect中更改,不会直接返回在视图中,它得等下一次更新,因为useEffect是更新之后执行的。

2.1.2 example07-3

import React, {useState, useMemo, useEffect} from 'react';
 
function Memo() {
    const [name,setName] = useState("leo");
    const [age,setAge] = useState(10);
    let age2 = useMemo(()=>{
        console.log(1);
        return age < 18?"未成年":"成年人";
    },[age]);
    return (<div>
        姓名:{name},<br />
        年龄:{age},<br />
        年龄阶段:{age2},<br />
        <button onClick={()=>{
            setAge(age+2);
        }}>长大</button>
    </div>);
}
 
export default Memo;
 

感觉跟概念不一致,每次更新的时候,方法还是在执行啊?

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第4张图片

因为在这里仅仅是依赖age,但是没有设置详细的依赖条件。

现在我们设置age < 18刚开始,这个返回true,直到变为false才会调用,之后状态一直是false了,不会再切回true了,因此也不会再调用该函数了。

import React, {useState, useMemo, useEffect} from 'react';
 
function Memo() {
    const [name,setName] = useState("leo");
    const [age,setAge] = useState(10);
    let age2 = useMemo(()=>{
        console.log(1);
        return age < 18?"未成年":"成年人";
    },[age < 18]);
    return (<div>
        姓名:{name},<br />
        年龄:{age},<br />
        年龄阶段:{age2},<br />
        <button onClick={()=>{
            setAge(age+2);
        }}>长大</button>
    </div>);
}
 
export default Memo;
 

这就是useMemo的应用了,它能极大地优化我们的计算性能。

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第5张图片

参考:https://github.com/6xiaoDi/Projec-React-hooks-enterprise-level-mbapp/tree/as0.07-3
Branch: bhStudy-Hooks

commit description:as0.07-3-example07-3(useMemo ——应用原理)

tag:as0.07-3

建议如果涉及复杂运算的函数,又仅仅依赖几个状态的改变,就可以用useMemo来优化了。

3. useCallback

它是useMemo的变体,并且极其相似。

3.1 example08

useMemo除了第一个参数是函数外,这个函数里还可以嵌套useMemo,即函数返回一个useMemo,或者可以返回一个函数。

import React, {useState, useMemo} from 'react';
 
function CallBack() {
    const [name,setName] = useState("leo");
    const [age,setAge] = useState(10);
    let age2 = useMemo(()=>{
        
        // 返回一个函数
        return ()=>age<18?"未成年":"成年人";
    },[age < 18]);
 
    return (<div>
        姓名:{name},<br />
        年龄:{age},<br />
        年龄阶段:{age2()},<br />
        <button onClick={()=>{
            setAge(age+2);
        }}>长大</button>
    </div>);
}
 
export default CallBack;
 

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第6张图片

运行可能没啥问题,但是在处理复杂业务的时候,有可能useMemo第一个函数参数返回的是一个useMemo或者是一个函数,这样代码就有点复杂,因此我们可以对其进行简化。

直接把返回的函数内容传给useCallback即可。

import React, { useState, useCallback} from 'react';
 
function CallBack() {
    const [name,setName] = useState("leo");
    const [age,setAge] = useState(10);
    let age2 = useCallback(()=>age<18?"未成年":"成年人",[age < 18]);
    return (<div>
        姓名:{name},<br />
        年龄:{age},<br />
        年龄阶段:{age2()},<br />
        <button onClick={()=>{
            setAge(age+2);
        }}>长大</button>
    </div>);
}
 
export default CallBack;

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第7张图片

参考:https://github.com/6xiaoDi/Projec-React-hooks-enterprise-level-mbapp/tree/as0.08
Branch: bhStudy-Hooks

commit description:as0.08-example08(useCallback)

tag:as0.08

3.2 [useMemo和useCallback的区别 及使用场景]

useMemouseCallback 接收的参数都是一样,第一个参数为回调 第二个参数为要依赖的数据

共同作用:
1.仅仅 依赖数据 发生变化, 才会重新计算结果,也就是起到缓存的作用。

两者区别:
1.useMemo 计算结果是 return 回来的值, 主要用于 缓存计算结果的值 ,应用场景如: 需要 计算的状态
2.useCallback 计算结果是 函数, 主要用于 缓存函数,应用场景如: 需要缓存的函数,因为函数式组件每次任何一个 state 的变化 整个组件 都会被重新刷新,一些函数是没有必要被重新刷新的,此时就应该缓存起来,提高性能,和减少资源浪费。

4. useContext

context
createContext、Provider、Consumer、contextType

Context作用:跨层传递信息

4.1 example09

4.1.1 example09-1

之前我们组件间传递消息是首先会有一个子组件,先演示类组件

code\project\study-hooks\react-app\src\hooks\context.js

import React, {Component} from 'react';
 
class Child extends Component {
    render(){
        return (
            <strong>这是祖先传下来的宝贝: {this.props.info}</strong>
        )
    }
}
 
class Parent extends Component {
    render(){
        return (<p>
            <Child info={this.props.info}/>
        </p>);
    }
}
 
class Context extends Component {
   render(){
       return <div>
           <Parent
               info ={"今天天气不错!"}
           />
       </div>
   }
}
 
export default Context;
 

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第8张图片

参考:https://github.com/6xiaoDi/Projec-React-hooks-enterprise-level-mbapp/tree/as0.09-1
Branch: bhStudy-Hooks

commit description:as0.09-1-example09-1(useContext ——类组件消息传递)

tag:as0.09-1

4.1.2 example09-2

观察以上过程还是相对非常麻烦的,可能有人就去用redux,但是react能不能有便捷方法呢?这就用到了createContext这个方法了。

import React, {Component, createContext} from 'react';
 
console.log(createContext())

我们先看一下它的返回值。它返回一个对象,里面主要有两个对象,一个是Consumer,一个是Provider。它实际就是通过Consumer和Provider传递消息的。

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第9张图片

我们先将ConsumerProvider解构出来。

Provider包装父组件传递参数,接收的对象,用Consumer包装,Consumer只能接收一个函数,从context参数中取参数。

import React, {Component, createContext} from 'react';
 
let {Provider,Consumer} = createContext();
 
class Child extends Component {
    render(){
        return (
                <Consumer>
                    {(context)=>{
                        console.log(context);
                        return <strong>这是祖先传下来的宝贝:{context.info} </strong>
                    }}
                 </Consumer>
        )
    }
}
 
class Parent extends Component {
    render(){
        return (<p>
            <Child/>
        </p>);
    }
}
 
class Context extends Component {
   render(){
       return <div>
           <Provider value={{info :"今天天气不错"}}>
               <Parent />
           </Provider>
       </div>
   }
}
 
export default Context;
 

这就传递成功了,如果还有其他地方需要使用的话,不管传递几层,只需要Consumer包装即可。(React16.3新增的CreateContext

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第10张图片

参考:https://github.com/6xiaoDi/Projec-React-hooks-enterprise-level-mbapp/tree/as0.09-2
Branch: bhStudy-Hooks

commit description:as0.09-2-example09-2(useContext ——类组件消息传递-CreateContext使用)

tag:as0.09-2

4.1.3 example09-3

Provider负责向下传递数据,Consumer负责在子孙级中去接收Provider传下的数据,以上写法还是有些麻烦,有没有更简易的写法呢?我们看看类式组件更简便的写法。

不用Consumer传递数据,在类式组件中可以申明一个静态属性contextType给它赋值后,其实它就是类式组件中context属性。

import React, {Component, createContext} from 'react';
 
let myContext = createContext();
 
class Child extends Component {
    // 不用Consumer传递数据,在类式组件中可以申明一个静态属性contextType给它赋值后,其实它就是类式组件中context属性
    static contextType = myContext;
    render(){
        console.log(this.context);
        return (
            <strong>这是祖先传下来的宝贝: {this.context.info}</strong>
        )
    }
}
 
 
class Parent extends Component {
    render(){
        return (<p>
            <Child/>
        </p>);
    }
}
 
class Context extends Component {
   render(){
       return <div>
           <myContext.Provider value={{info :"今天天气不错"}}>
               <Parent />
           </myContext.Provider>
       </div>
   }
}
 
export default Context;

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第11张图片

参考:https://github.com/6xiaoDi/Projec-React-hooks-enterprise-level-mbapp/tree/as0.09-3
Branch: bhStudy-Hooks

commit description:as0.09-3-example09-3(useContext ——类组件消息传递-CreateContext简便方法使用)

tag:as0.09-3

4.1.4 example09-4

用函数式组件使用Contex

import React, {Component, createContext} from 'react';
 
let myContext = createContext();
 
class Child extends Component {
    // 不用Consumer传递数据,在类式组件中可以申明一个静态属性contextType给它赋值后,其实它就是类式组件中context属性
    static contextType = myContext;
    render(){
        return (
            <strong>这是祖先传下来的宝贝: {this.context.info}</strong>
        )
    }
}
 
function Child2(){
    return (
        <myContext.Consumer>
            {
                context=>{
                    console.log(context);
                    return <div><strong>这是祖先传下来的宝贝: {context.info}</strong></div>
                }
            }
        </myContext.Consumer>
 
    )
}
 
class Parent extends Component {
    render(){
        return (<div>
            <Child/>
            <Child2/>
        </div>);
    }
}
 
class Context extends Component {
   render(){
       return <div>
           <myContext.Provider value={{info :"今天天气不错"}}>
               <Parent />
           </myContext.Provider>
       </div>
   }
}
 
export default Context;
 

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第12张图片

参考:https://github.com/6xiaoDi/Projec-React-hooks-enterprise-level-mbapp/tree/as0.09-4
Branch: bhStudy-Hooks

commit description:as0.09-4-example09-4(useContext ——类组件消息传递-CreateContext在函数式组件中的使用)

tag:as0.09-4

4.1.5 example09-5

但是每次都用Consumer包装会很麻烦,我们寻找一个简便方式借助钩子中的useContextuseContext必须传入参数,确定它是哪一个context的对象,其作用就是接受context传来的信息。

import React, {Component, createContext, useContext} from 'react';
 
let myContext = createContext();
 
class Child extends Component {
    // 不用Consumer传递数据,在类式组件中可以申明一个静态属性contextType给它赋值后,其实它就是类式组件中context属性
    static contextType = myContext;
    render(){
        return (
            <strong>这是祖先传下来的宝贝: {this.context.info}</strong>
        )
    }
}
 
function Child2(){
    let {info} = useContext(myContext);
    return (
        <div><strong>这是祖先传下来的宝贝: {info} </strong></div>
    )
}
 
class Parent extends Component {
    render(){
        return (<div>
            <Child/>
            <Child2/>
        </div>);
    }
}
 
class Context extends Component {
   render(){
       return <div>
           <myContext.Provider value={{info :"今天天气不错"}}>
               <Parent />
           </myContext.Provider>
       </div>
   }
}
 
export default Context;

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第13张图片

参考:https://github.com/6xiaoDi/Projec-React-hooks-enterprise-level-mbapp/tree/as0.09-5
Branch: bhStudy-Hooks

commit description:as0.09-5-example09-5(useContext ——类组件消息传递-CreateContext在函数式组件中的使用-useContext)

tag:as0.09-5

context的使用基本就是这么多,但是在实际项目中需要注意小迪不建议直接使用Context,除非对React的数据流非常熟悉,否则别使用它,还是建议使用React-Redux

本身React-Redux就是基于Context实现的。在HooksuseContext,和配合 useReducer,可以实现一个简易的Redux

5. useReducer

5.1 example10

实现一个简易的Redux

5.1.1 example10-1

Redux开发实际要基于一个纯函数reducer开发

import React, {useReducer} from 'react';
 
// state 状态 action 操作动作
function reduce(state = 0,action){
    switch(action.type){
        case "add":
            state += 1
            break;
        case "minus":
            state -= 1
            break;       
    }
    return state;
}
 
function Reduce(){
    // 第一个参数reduce,第二个参数是函数初始值
    console.log(useReducer(reduce,0))
    return (
        <div>
 
        </div>
    );
}
 
export default Reduce;

返回值,第一个是state值,第二个是dispatch,修改state的方法。

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第14张图片

参考:https://github.com/6xiaoDi/Projec-React-hooks-enterprise-level-mbapp/tree/as0.10-1
Branch: bhStudy-Hooks

commit description:as0.10-1-example10-1(useReducer ——查看useReducer返回值)

tag:as0.10-1

5.1.2 example10-2

Redux是在最外层把状态传递进去,哪一层需要,就对哪一层进行包装,然后包装数据。

实现简易redux,没有高阶组件封装

import React, {useReducer, createContext, useContext} from 'react';
 
let myContext = createContext();
 
// state 状态 action 操作动作
function reduce(state = 0,action){
    switch(action.type){
        case "add":
            state += 1
            break;
        case "minus":
            state -= 1
            break;       
    }
    return state;
}
 
function Child(){
    let {state,dispatch} = useContext(myContext);
    return (
        <div>
            {/*减法*/}
            <button
                onClick={()=>{
                    dispatch({
                        type: "minus"
                    });
                }}
            >-</button>
            <span> {state}  </span>
            {/*加法*/}
            <button
                onClick = {()=>{
                    dispatch({
                        type: "add"
                    });
                }}
            >+</button>
        </div>
    )
}
 
function Reduce(){
    // 第一个参数reduce,第二个参数是state初始值
    let [state,dispatch] = useReducer(reduce,0);
    return (
        <myContext.Provider value = {{
            state,
            dispatch
        }}>
            <Child />
        </myContext.Provider>
    );
}
 
export default Reduce;

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第15张图片

参考:https://github.com/6xiaoDi/Projec-React-hooks-enterprise-level-mbapp/tree/as0.10-2
Branch: bhStudy-Hooks

commit description:as0.10-2-example10-2(useReducer ——实现简易redux)

tag:as0.10-2

6. 自定义hook

6.1 example11

很多操作都有上一步和下一步这个操作,如切换选项卡、幻灯片上一张下一张,这些操作都是最终一个数字的改变。

我们把这个通用逻辑方式摘出来,写成一个hooks

要求hooks函数组件名称必须以use开头,过程和返回值可以根据自己的需求去定义。

上一张和下一张改变数字,都有哪些改变呢?

实际上是在当前数字的基础上,让它进行加减。useCount除了传当前值,还需要有最大值和最小值作为限制。目前先不弄这么复杂,就做最基本的逻辑,即加减。

注意在自定义的hooks中也可调用其他的hooks

import React, {useState} from 'react';
 
function useCount(init){
    let [count,setCount] = useState(init);
    function add(){
        count++;
        setCount(count);
    }
    function minus(){
        count--;
        setCount(count);
    }
    return [count,add,minus,setCount];
}
 
function Hook(){
    let [count,add,minus,setCount] = useCount(0);
    return (<div>
        <button
            onClick={()=>{
                minus();
            }}
        >-</button>
        <span> {count}  </span>
        <button
            onClick = {()=>{
                add();
            }}
        >+</button>
        <button onClick={()=>{
            setCount(5);
        }}>自定义设置</button>
    </div>);
}
 
export default Hook;
 

React Hooks快速入门(前提:React基础)(2)——对比详解计算优化之UseMemo和useCallback、useContext、自定义hooks(附详细案例源码解析过程及版本迭代过程)_第16张图片

参考:https://github.com/6xiaoDi/Projec-React-hooks-enterprise-level-mbapp/tree/as0.11
Branch: bhStudy-Hooks

commit description:as0.11-example11(自定义hooks)

tag:as0.11

6.2 Hook 使用规则

  • 只在最顶层(组件的最顶层,不能在if或者循环中、子函数使用hooks)使用 Hook(Hook本身包括副作用可能会有一些相关依赖,即不写在最顶层,中间会造成一些依赖导致出一些错误)

  • 只在 React函数中调用 Hook

    • React函数组件中
    • React(自定义)Hook

    (如在类组件中使用hook可能会报错)



(后续待补充)

你可能感兴趣的:(#,React,Hooks)