四个问题详解

问题一:git rebase和git merge区别

git rebase:
指令:git rebase -i  [startpoint]  [endpoint] (如不指定起止点,则默认是当前分支head指向的commit)
作用:git rebase操作实际上是将当前执行rebase分支的所有基于原分支提交点之后的commit打散成一个一个的patch,并重新生成一个新的commit hash值,再次基于原分支目前最新的commit点上进行提交,并不根据两个分支上实际的每次提交的时间点排序,rebase完成后,切到基分支进行合并另一个分支时也不会生成一个新的commit点,可以保持整个分支树的完美线性。
git merge:
指令:git merge (--no-ff) 需要合并的分支名  (tip: 加上--no-ff可以保存你之前的分支历史。能够更好的查看 merge历史,以及branch 状态,不加则不会显示feature,只保留单条分支记录)
作用:git merge 操作合并分支会让两个分支的每一次提交都按照提交时间(并不是push时间)排序,并且会将两个分支的最新一次commit点进行合并成一个新的commit,最终的分支树呈现非整条线性直线的形式。
两个区别:

问题二:防抖和节流原理,应用场景

1. 防抖:

1) 原理:触发事件后在 n 秒内函数只能执行一次,如果在n秒内又**触发**了事件,则会**重新**计算函数执行时间。
2) 简例:
问题:当持续触发scroll事件时,事件处理函数handle只在停止滚动1000毫秒之后才会调用一次,也就是说在持续触发scroll事件的过程中,事件处理函数handle一直没有执行。
实现:
// 防抖
function debounce(func, wait=0) {    
 if (typeof func !== 'function') {
  throw new TypeError('need a function arguments')
 }
 let timeid = null;
  let result;
 
 return function() {
  let context = this;
  let args = arguments;
     if (timeid) {
       clearTimeout(timeid);
     }
     timeid = setTimeout(function() {
       result = func.apply(context, args);
     }, wait);
     return result;
  }
}
// 处理函数
function handle() {    
    console.log(Math.random()); 
}
// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000));
3) 应用场景:
  search搜索联想,用户在不断点击搜索按钮时,用防抖来节约请求资源。
  window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次

2. 节流

1) 原理:指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。
	一般有两种方法:时间戳版和定时器版
2) 简例:
html文件:
<div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
<script>
    let num = 1;
    let content = document.getElementById('content');

    function count() {
        content.innerHTML = num++;
    };
    content.onmousemove = count;
</script>
时间戳版:
function throttle(func, wait) {
  let previous = 0;
  return function() {
    let now = Date.now();
    let context = this;
    let args = arguments;
    if (now - previous > wait) {
      func.apply(context, args);
      previous = now;
    }
  }
}
定时器版:
function throttle(func, wait) {
  let timeout;
  return function() {
    let context = this;
    let args = arguments;
    if (!timeout) {
      timeout = setTimeout(() => {
      timeout = null;
      func.apply(context, args)
      }, wait)
    }
  }
}
使用方法:
content.onmousemove = throttle(count,1000);
3) 应用场景:
  鼠标不断点击触发,mousedown(单位时间内只触发一次)
  监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断

问题三:webpack(重点process和plugin)

$$ 常用配置:
module.exports = {
cache: true, //开启缓存功能,这样只有变化的文件才会重新加载,可提升构建速度
entry:{},
output: {},
module: {},
resolve: {},
externals:{},
plugins:{}
}

1. plugins

  1. 描述:是除module外最为重要的配置了,插件机制提供了loader无法完成的功能。
  1. 常用的plugin有:
    CommonsChunkPlugin
    DefinePlugin
    HtmlWebpackPlugin
    UglifyjsWebpackPlugin
    ExtractTextWebpackPlugin
    OptimizeCSSPlugin
    CompressionWebpackPlugin
    SourceMapDevToolPlugin
    FriendlyErrorsPlugin
    BundleAnalyzerPlugin
  1. 插件用法配置
    CommonsChunkPlugin
    使用:new webpack.optimize.CommonsChunkPlugin(options)
    options的配置:
    name:抽取出公共代码后的模块名,若没有filename项,该项也决定打包后的文件名
    chunks:从哪些模块中抽取公共代码,若省略,则从所有模块中抽取(一般是从入口模块抽)
    minChunks:number|Infinity|function(module, count) -> boolean
    为数字A时,表示模块必须被引用超过A次才提取到公共代码里;为infinity时,表示无穷,不抽取任何模块;为函数时,函数返回true就抽取,实例:
    new webpack.optimize.CommonsChunkPlugin({
    name: ‘vendor’,
    minChunks: function(module, count) {
    // any required modules inside node_modules are extracted to vendor
    return (
    module.resource && /.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, ‘…/node_modules’) ) === 0 )
    }
    })
  1. 具体plugin介绍:
  • DefinePlugin
    允许你创建一个在编译时可以配置的全局常量。
    new webpack.DefinePlugin({
    ‘process.env’: config.dev.env
    })
    注意这里定义的常量是在业务模块中使用的,并不是在webpack这些配置文件中使用,因为webpack本身运行在node环境下,node中自身就有process等全局变量。如在入口main.js中不用声明process.env就可以直接使用process.env判断环境
  • HtmlWebpackPlugin
    生成一个HTML5文件,该文件的 body 中使用script 标记引用了所有 webpack bundle。若有多个webpack入口点,它们都将包含在生成的HTML中,使用script标记引用。如果有任何CSS资源在webpack的 output 中,(例如,使用ExtractTextPlugin提取的CSS)那么这些资源将被包含在HTML head 中,使用 标记引用。
    实例:
    var HtmlWebpackPlugin = require(‘html-webpack-plugin’);
    var webpackConfig = {
    entry: ‘index.js’,
    output: {
    path: ‘dist’,
    filename: ‘index_bundle.js’
    },
    plugins: [new HtmlWebpackPlugin()]
    };
    //生成一个文件 dist/index.html
<head>
	<meta charset="UTF-8">
	<title>webpack App</title>  
 </head>  
 <body>
  	<script src="index_bundle.js">
  	</script>   
</body> 
</html>

配置:
title: 生成的html中的title
filename: 生成的html的名字
template: 用哪个模板来生成html
inject:true | ‘head’ | ‘body’ | false 取true或body时,all javascript 放在body底部,'head’放head里
favicon:指定生成的html的favicon
minify:{…} | false 压缩生成的html
hash: true | false 是否给所有scripts和css添加hash
cache: 只有当html中内容变化了才重新输出,默认true
showErrors:true | false 错误细节是否写到html,默认true
chunks: 只添加哪些chunk
excludeChunks: 去掉哪些chunk

  • UglifyjsWebpackPlugin
    new webpack.optimize.UglifyJsPlugin({
    compress: {
    warnings: false
    },
    sourceMap: true
    })
    // 或使用这个代替
    var ParallelUglifyPlugin = require(‘webpack-parallel-uglify-plugin’)
    new ParallelUglifyPlugin({
    cacheDir: ‘.cache/’,
    uglifyJS: {
    output: {
    comments: false,
    beautify: false
    },
    compress: {
    warnings: false,
    drop_console: true
    }
    }
    }),
    配置项和uglify一样的
  • ExtractTextWebpackPlugin
    将所有的 入口chunk (entry chunks) 中的 require(“style.css”) 或import ‘style.css’ 移动到分开的 css 文件.
    需要两步:1、rules中配置 ExtractTextPlugin.extract 2、plugins中配置new ExtractTextPlugin(“styles.css”)
    const ExtractTextPlugin = require(“extract-text-webpack-plugin”);
    rules: [
    {
    test: /.cssKaTeX parse error: Expected 'EOF', got '}' at position 116: …" }) }̲ ] }, plugins…|.html$/,
    threshold: 10240,
    minRatio: 0.8
    })
    ]
    asset: 目标资源名称。 [file] 会被替换成原始资源。[path] 会被替换成原始资源的路径, [query] 会被替换成查询字符串。默认值是 “[path].gz[query]”。
    algorithm: 可以是 function(buf, callback) 或者字符串。对于字符串来说依照 zlib 的算法(或者 zopfli 的算法)。默认值是 “gzip”。
    test: 所有匹配该正则的资源都会被处理。默认值是全部资源。
    threshold: 只有大小大于该值的资源会被处理。单位是 bytes。默认值是 0。
    minRatio: 只有压缩率小于这个值的资源才会被处理。默认值是 0.8。
  • BundleAnalyzerPlugin
    分析打包构成
    var BundleAnalyzerPlugin = require(‘webpack-bundle-analyzer’).BundleAnalyzerPlugin;
    plugins: [
    new BundleAnalyzerPlugin({
    // Can be server, static or disabled.
    // In server mode analyzer will start HTTP server to show bundle report.
    // In static mode single HTML file with bundle report will be generated.
    // In disabled mode you can use this plugin to just generate Webpack Stats JSON file by setting generateStatsFile to true.
    analyzerMode: ‘server’,
    // Host that will be used in server mode to start HTTP server.
    analyzerHost: ‘127.0.0.1’,
    // Port that will be used in server mode to start HTTP server.
    analyzerPort: 8888,
    // Path to bundle report file that will be generated in static mode.
    // Relative to bundles output directory.
    reportFilename: ‘report.html’,
    // Module sizes to show in report by default.
    // Should be one of stat, parsed or gzip.
    // See “Definitions” section for more information.
    defaultSizes: ‘parsed’,
    // Automatically open report in default browser
    openAnalyzer: true,
    // If true, Webpack Stats JSON file will be generated in bundles output directory
    generateStatsFile: false,
    // Name of Webpack Stats JSON file that will be generated if generateStatsFile is true.
    // Relative to bundles output directory.
    statsFilename: ‘stats.json’,
    // Options for stats.toJson() method.
    // For example you can exclude sources of your modules from stats file with source: false option.
    // See more options here: https://github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
    statsOptions: null,
    // Log level. Can be ‘info’, ‘warn’, ‘error’ or ‘silent’.
    logLevel: ‘info’
    })
    ]

2. process

通常情况下,我们需要针对不同环境(开发环境、集成环境、生产环境等),进行相应策略的打包(比如是否替换接口地址,代码是否压缩等)。webpack就是通过process.env属性加以区分。
webpack是npm生态中的一个模块,webpack的运行依赖于node的环境,没有node是不能打包的。这里的process.env就是Nodejs提供的一个API,它返回一个包含用户环境信息的对象。如果我们给Nodejs 设置一个环境变量,并把它挂载在 process.env 返回的对象上,便可以在代码中进行相应的环境判断。
process.env
process.env属性返回一个对象,包含了当前Shell的所有环境变量。比如,process.env.HOME返回用户的主目录。
通常的做法是,新建一个环境变量NODE_ENV,用它确定当前所处的开发阶段,生产阶段设为production,开发阶段设为development或staging,然后在脚本中读取process.env.NODE_ENV即可。
要说明的是,NODE_ENV 这个名称只是开发社区的一种共识,名称内容是可以修改的。如果需要,你也可以把它定义为 NODE_abc或者xxx都行。下边我们按照约定就以NODE_ENV来说。
在Webpack配置文件中,经常会看到如下类似的代码:
webpack.config.js
module.exports = {
mode: process.env.NODE_ENV === ‘production’ ? ‘production’ : ‘development’
}

process.env是Nodejs提供的一个API,那么如果想用process.env.NODE_ENV,就必须先设置其值才能用。
但是如何设置 这个process.env.NODE_ENV环境变量呢?在webpack项目里,我们可以通过设置package.json来实现,但是Windows 系统和Mac系统有区别。
Windows 系统
// package.json
{

“scripts”: {
“dev”: “set NODE_ENV=development && webpack-dev-server --open --hot”,
“build”: “set NODE_ENV=production && --progress --hide-modules”
}
}
Mac 系统
// package.json
{

“scripts”: {
“dev”: “export NODE_ENV=development && webpack-dev-server --open --hot”,
“build”: “export NODE_ENV=production && --progress --hide-modules”
}
}
但它们的语法都不尽相同。这就带来两个问题:
那么问题又来了,我在Windows 开发部署的项目,可能在 Mac 系统无法正常打包,反之亦然。为了解决这个问题,有人就开发了 cross-env。
cross-env是一个跨平台设置环境变量的第三方包,它可以让你只配置一行命令,就能轻松地在多个平台设置环境变量。首先先安装
npm install --save-dev cross-env
然后配置package.json就可以了
// package.json
{

“scripts”: {
“dev”: “cross-env NODE_ENV=development webpack-dev-server --open --hot”,
“build”: “cross-env NODE_ENV=production webpack --mode=production --progress --hide-modules”
},
}
这样我们就可以在项目里取到process.env.NODE_ENV的值,然后利用这个值来区分当前环境。

问题四:hooks

Hooks是 React v16.8 的新特性,可以在不使用类组件的情况下,使用 state 以及其他的React特性;

Hooks是完全可选的,无需重写任何已有代码就可以在一些组件中尝试Hook
于React v16.8发布,100%向后兼容,Hooks不包含任何破坏性改动.
React也没有计划移除class类组件,而且Hooks不会影响对React的理解,它为已知的React概念提供了更直接的API

Hooks解决的问题

  1. 函数式组件不能使用state:函数式组件比类组件更简洁好用,而Hooks让它更加丰富强大;
  2. 副作用问题:诸如数据获取、订阅、定时执行任务、手动修改ReactDOM这些行为都可以称为副作用;而Hooks的出现可以使用 useEffect 来处理这些副作用;
  3. 有状态的逻辑重用组件
  4. 复杂的状态管理:之前通常使用 redux、dva、mobx 这些第三方状态管理器来管理复杂的状态,而Hooks可以使用 useReducer、useContext 配合实现复杂的状态管理;
  5. 开发效率和质量问题:函数式组件比类组件简洁,效率高,性能也好。

useState
useState:组件状态管理的钩子
import { useState } from ‘react’
const [state, setState] = useState(initState)
state:管理组件的状态;
setState:更新state的方法,方法名不可更改!
initState:初始的state,可以是任意的数据类型(只在初次渲染时有效,二次渲染时会被忽略),也可以是回调函数,但函数必须有返回值。

函数式组件实现计数器
import { useState } from 'react'
export default function App() {
    const [count, setCount] = useState(0)  // 初始值0
    return (
        <div>
            <div>点击了{ count }</div>
            <button onClick={()=>setCount(count+1)}>点击</button>
        </div>
    ) 
}

useState: 让函数式组件具备了管理状态的能力,不再是一个无状态组件;与class的setState类似,当向useState更新状态的方法setCount传递一个函数时,此函数会接收到上一次的状态:setCount(prevCount => prevCount + 1)
但与class的setState不同的是,如果状态值是一个对象,useState更新状态的方法是不会合并更新对象的,可以结合展开运算符来达到合并更新对象的效果:
setState(prevState => {
return { …prevState, …updateValues };
});
useState当然可以多次调用,从而定义多个状态值;但不管调用多少次,相互之间都是独立的,不会相互污染。这就是为什么React否掉了Mixins,因为Mixins机制让多个Mixins共享一个对象的数据空间,这样就很难保证不同Mixins依赖的状态不发生冲突;
function App() {
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState(‘banana’);
const [todos, setTodos] = useState([{ text: ‘Learn Hooks’ }]);
}
React又是如何保证多个useState的相互独立呢,定义时并没有告诉React这些值的key,React如何保证这三个useState能准确找到它对应的state呢 —> 根据useState出现的顺序!
第一次渲染
useState(42); //将age初始化为42
useState(‘banana’); //将fruit初始化为banana
useState([{ text: ‘Learn Hooks’ }]); //…
第二次渲染
useState(42); //读取状态变量age的值(这时候传的参数42直接被忽略)
useState(‘banana’); //读取状态变量fruit的值(这时候传的参数banana直接被忽略)
useState([{ text: ‘Learn Hooks’ }]); //…
加入条件控制语句
let showFruit = true;
function App() {
const [age, setAge] = useState(42);
if(showFruit) {
const [fruit, setFruit] = useState(‘banana’);
showFruit = false;
}
const [todos, setTodos] = useState([{ text: ‘Learn Hooks’ }]);
}
这样一来,首次渲染的初始化过程是不变的,但第二次渲染就有所不同了
第二次渲染
useState(42); //读取状态变量age的值(这时候传的参数42直接被忽略)
// useState(‘banana’);
useState([{ text: ‘Learn Hooks’ }]); //读取到的却是状态变量fruit的值,导致报错
鉴于此,React规定:
Hooks必须写在函数的最外层,不能在循环语句、条件判断、子函数中调用;
只能在React的函数式组件、自定义Hooks中调用Hooks,不能在其他JavaScript函数中调用。

useEffect
useEffect(callback, array):副作用处理的钩子;它也是componentDidMount()、componentDidUpdate()、componentWillUnmount()、这几个生命周期方法的统一,一个顶三个!React 会等待浏览器完成画面渲染之后才会延迟调用 useEffect,而生命周期钩子是同步执行的
callback 回调函数,作用是处理副作用的逻辑,可以返回一个函数,用作清理副作用;
import { useEffect } from ‘react’
useEffect(() => {
…//副作用处理
return () => {
…//清理副作用的清除函数
}
}, [])
为防止内存泄漏,清除函数会在组件卸载前执行;如果组件多次渲染,则在执行下一个 effect 之前,上一个 effect 就已被清除;
array 可选数组,用于控制useEffect的执行; 省略时,每次渲染都会执行; 空数组时,只会在组件挂载/卸载时执行一次,类似componentDidMount、componentWillUnmount; 非空数组时,会在数组元素发生改变后执行,且这个变化的比较是浅比较。
提供第二个参数时,相当于告诉React 该组件不依赖于state/props 的变化,只依赖第二个参数的变化。

function App() {
    const [count, setCount] = useState(0)  // 初始值0
    useEffect(()=>{
        console.log('1')  // 初次渲染时执行一次,之后每次 count 变化都执行
        return () => {
            console.log('2')  // 初次渲染时不执行,之后每次 count 变化都最先执行
        }
    }, [count])
    return (<div>
         <div>点击了{ count }</div>
         <button onClick={() => setCount(preCount => preCount+1)}>点击</button>
    </div>) 
}

// 初次渲染:1
// 点击更新count:2 1
父组件也可以通过props控制子组件是否执行useEffect
useEffect(()=>{
// 注册事件
const subscription = props.source.subscribe();
return ()=>{
// 解绑事件
subscription.unsubscribe();
}
}, [props.source]);
如前文所述,Hooks可以反复多次使用,相互独立。所以合理的做法是,给每一个副作用加一个单独的useEffect钩子。这样一来,这些副作用不再全堆在class组件的生命周期钩子里,代码变得更加清晰;
useEffect中定义的副作用函数在执行时不会阻碍浏览器更新视图,即这些函数是异步执行的,而class组件中的生命周期钩子都是同步执行的;异步设计对大多数副作用是合理的,但也有特例,比如有时候需要先根据DOM计算出某个元素的尺寸,然后再去渲染,此时则希望二次渲染是同步发生的,也就是在浏览器真的去绘制界面前发生;
为此,React 为此提供了一个额外的 useLayoutEffect Hook 来处理这类 effect。它和 useEffect 的结构相同,区别只是调用时机不同 – useLayoutEffect 是同步的。

useContext
useContext():同一个父组件的后台组件之间的全局数据共享;
useContext() 接收React.createContext()的返回值作为参数,即context对象,并返回最近的context。当最近的context更新时,使用该context的 Hooks 将会重新渲染;
创建一个Context文件 InfoContext.js
// 设置了默认值 defaultValue
const InfoContext = React.createContext({ name: ‘Jerry’, age: 18 })
export default InfoContext

父组件
import InfoContext from './context/InfoContext'
const Person = () => {
    const ctx = useContext(InfoContext)
    return(<InfoContext.Provider value={{ username: 'superman' }}>
       <div>
            <AgeCompt></AgeCompt>
        </div>
    </InfoContext.Provider>)
}
export default Person
某一层的后代组件
import { useContext } from 'react'
import InfoContext from './context/InfoContext'
const AgeCompt = () => {
    const { username } = useContext(InfoContext)
    return <p>{username}</p>
}
export default AgeCompt
  • useReducer():useState的一个增强体,用于处理复杂的状态管理,灵感来源于Redux的reducer
  • useState: 内部就是基于 useReducer 实现的,只是对于简单的状态管理,useState()比较好用;
    const [state, setState] = useState(initState)
    const [state, dispatch] = useReducer(reducer, initState, initAction)
  • reducer: 一个函数,根据action状态处理并更新state
  • initState: 初始化state
  • initAction: useReducer()初次执行时被处理的action,会把第二个参数initState当作参数执行
  • state: 状态值
  • dispatch: 更新state的方法,接收action作为参数,当它被调用时,reducer函数也会被调用,同时根据action去更新state,action是一个描述操作的对象,如 dispatch({type: ‘add’})
import { useReducer } from 'react';
const initState = { count: 0 };
const initAction = initState => { count: initState.count + 2 };
const reducer = (state, action) => {
    switch(action.type) {
        case 'ADD':
            return { count: state.count+1 }
        case 'DEL':
            return { count: state.count-1 }
        case 'RESET':
            return initState
        default:
            return state
    }
}
export default function UserCompt() {
    const [state, dispatch] = useReducer(reducer, initState)
    return (<div>
        <p>{state.count}</p>
        <div>
            <button onClick={() => dispatch({ type: 'ADD', /**可以加一些其他属性*/ })}>增加</button>
            <button onClick={() => dispatch({ type: 'DEL' })}>减少</button>
            <button onClick={() => dispatch({ type: 'RESET' })}>重置</button>
        </div>
    </div>)
}

更多Hooks
useMemo(callback, array):性能优化,利用了闭包的特性,通过记忆值来避免在每个渲染上都执行高开销的计算(计算缓存);适用于复杂的计算场景,如复杂的列表渲染,对象深拷贝…
callback - 用于处理逻辑的函数
array - 依赖项,控制useMemo()重新执行的数组,array元素改变时才会重新执行useMemo()
返回值是一个记忆值,也是callback的返回值
import React, { useMemo } from ‘react’;
export default function UserCompt() {
const obj1 = { name: ‘Tom’, age: 15 }
const obj2 = { name: ‘Jerry’, age: 18, sex: ‘男’ }
//合并obj1、obj2
const memoValue = useMemo(() => Object.assign(obj1, obj2), [obj1, obj2])
return (


{ memoValue.name }


{ memoValue.age }


)
}
注意:不能在 useMemo() 中处理副作用逻辑,而是把副作用处理逻辑放在useEffect()
useCallback(callback, array):也是用于性能优化,与useMemo()不同的是,返回值是callback本身;
//合并obj1、obj2
const backValue = useCallback(() => Object.assign(obj1, obj2), [obj1, obj2])

<div>{ backValue().name } --- { backValue().age }</div>
当依赖项变化时,返回一个新的函数体callback。
useRef():创建ref,方便访问操作DOM
const RefCompt = () => {
    //创建ref
    const inputRef = useRef();
    const getValue = () => {
        //访问ref
        const inpt = inputRef.current;  // input的DOM对象
        inpt.focus();  // 让 input 框获取焦点
        console.log(inpt.value);  // input输入框的值
    };
    //挂载
    return (<div>
        <input ref={ inputRef } type="text" />
        <button onClick={ getValue }>获取值</button>
    </div>);
}

自定义Hooks
Hooks本质上就是封装好的勾子函数,在自定义Hooks时,最需要关心的就是性能、重复渲染这些问题;
自定义一个Effect Hooks,把可以复用的逻辑抽离出来,变成一个个可插拔的插销;
当标题变化时,则修改标题、否则不执行的Hooks, 例:

import { useEffect } from 'react'
//封装Hooks,以 use 开头
const useChangeTitle = (title) => {
    useEffect(() => {
        document.title = title
    }, [title])
}
export default (props) => {
    useChangeTitle("自定义修改标题Hooks")
    return <div>测试</div>
}

例:一个用来判断某个 id 是否在线的Hooks

import { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
    const [isOnline, setIsOnline] = useState(null);
    useEffect(()=>{
        //注册监听事件
        ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
        return () => {
          //取消监听事件
          ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
        };
    });
    return isOnline;
}

在组件中使用此Hooks

function FriendStatus(props) {
     //调用自定义Hooks
    const isOnline = useFriendStatus(props.friend.id);
    if (isOnline === null) {
       return 'Loading...';
    }
    return isOnline ? 'Online' : 'Offline';
}

Hooks的使用规则
只在顶层调用Hooks
Hooks的调用尽量只在顶层作用域
不要在循环、条件或嵌套函数中调用Hook,否则可能会无法确保每次组件渲染时都以相同的顺序调用Hook
只在函数组件调用Hooks:目前只支持函数式组件,未来版本Hooks会扩展到class类组件;
React Hooks的应用场景:函数式组件、自定义Hooks

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值