react hooks

  • react hooks
    • 出几道react hooks面试题
    • class组件存在哪些问题
    • 用useState实现state和setState功能
    • 用useEffect模拟组件生命周期
    • 用useEffect模拟WillUnMount时的注意事项
    • 其他Hooks
    • useRef和useContext
    • useReducer能代替redux吗
    • 使用useMemo做性能优化
    • 使用useCallback做性能优化
    • 什么是自定义Hook
    • 使用Hooks的两条重要规则
    • 为何Hooks要依赖于调用顺序
    • class组件逻辑复用有哪些问题
    • Hooks组件逻辑复用有哪些好处
    • Hooks使用中的几个注意事项
    • Hooks面试题解答

react hooks

出几道react hooks面试题

关于React Hooks
可选功能(class组件 vs Hooks)
100%向后兼容,没有破坏性改动
不会取代class组件,尚无计划要移除class组件

面试问到React Hooks
Hooks作为React的一部分,在面试中也只能占一部分时间
初学者还是以学好class组件为主,Hooks是加分项
学习Hooks的前提,必须学好class组件

本章的主要内容
State Hook
Effect Hook
其他 Hook
自定义Hook
组件逻辑复用
规范和注意事项

面试题
为什么会有React Hooks,它解决了哪些问题
React Hooks如何模拟组件生命周期
如何自定义Hook
React Hooks性能优化
使用React Hooks遇到哪些坑
Hooks相比HOC和Render Prop有哪些优点

class组件存在哪些问题

认识React Hooks
回顾React函数组件

State Hook
Effect Hook

函数组件的特点
没有组件实例
没有生命周期
没有state和setState,只能接收props

class组件的问题
大型组件很难拆分和重构,很难测试(即class不易拆分)
相同业务逻辑,分散带各个方法中,逻辑混乱
复用逻辑变得复杂,如Mixins、HOC、Render Prop

React组件更易用函数表达
React提倡函数式编程,view = fn(props)
函数更灵活,更易拆分,更易测试
但函数组件太简单,需要增强能力 — Hooks

用useState实现state和setState功能

让函数组件实现state和setState
默认函数组件没有state
函数组件是一个纯函数,执行完即销毁,无法存储state
需要State Hook,即把state功能“钩”到纯函数中

useState使用总结
useState(0)传入初始值,返回数组[state,setState]
通过state获取值
通过setState(1)修改值

Hooks命名规范
规定所有的Hooks都use开头,如useXxx
自定义Hook也要以use开头
非Hooks的地方,尽量不要使用useXxx写法

用useEffect模拟组件生命周期

让函数组件模拟生命周期
默认函数组件没有生命周期
函数组件是一个纯函数,执行完即销毁,自己无法实现生命周期
使用Effect Hook把生命周期“钩子" 到纯函数中

//模拟class组件的DidMount和DidUpdate
useEffect(() =>{console.log('在此发送一个ajax请求')
})
//模拟class组件的DidMount
useEffect(() =>{console.log('加载完了')
},[]) //第二个参数是[] (不依赖于任何state)//模拟class组件的DidUpdate
useEffect(() =>{console.log('更新了')
},[count,name])//第二个参数是依赖的state//模拟class组件的DidMount
useEffect(() =>{let timeId = window.setInterval(() =>{console.log(Date.now())},100)//返回一个函数//模拟WillUnMountreturn () => {window.clearInterval(timeId)}
},[])

useEffect使用总结
模拟componentDidMount - useEffect 依赖[]
模拟componentDidUpdate - useEffect无依赖,或者依赖[a,b]
模拟componentWillUnMount - useEffect中返回一个函数

useEffect让纯函数有了副作用
默认情况下,执行纯函数,输入参数,返回结果,无副作用
所谓副作用,即是对函数之外造成影响,如设置全局定时任务
而组件需要副作用,所有需要useEffect “钩” 到纯函数中

用useEffect模拟WillUnMount时的注意事项

【注意】模拟WillUnMount,但不完全相等


上两张图的效果是一样的,useEffect 返回的函数执行要看useEffect本身的作用起作用,如果useEffect本身即能模拟componentDidMount又能模拟componentDidUpdate,那componentWillUnMount和componentDidUpdate之前都会执行

useEffect中返回函数fn
useEffect依赖[],组件销毁时执行fn,等于WillUnMount
useEffect无依赖或依赖[a,b],组件更新时执行fn
即,下一次执行useEffect之前,就会执行fn,无论更新或卸载

小结
函数组件更适合React组件,但需要Hooks增强功能
useState可实现state和setState
useEffect可模拟组件主要的生命周期

其他Hooks

useRef
useContext
useReducer
useMemo
useCallback

useRef和useContext

useRef

import React,{useRef,useEffect} from 'React'function UseRef(){const btnRef = useRef(null) //初始值//不一定时获取DOM节点//const numRef = useRef(0)//numRef.currentuseEffect(() =>{console.log(btnRef.current) //DOM节点},[])return <div><button ref={btnRef}>click</button></div>
}
export default UseRef

useContext

import React,{useContext} from 'React'//主题颜色
const themes = {light:{foreground:'#000',background:'#eee'},dark:{foreground:'#fffbackground:'#222'}
}//创建Context
const ThemeContext = React.createContext(themes.light) //初始值
function ThemeButton(){const theme = useContext(ThemeContext)return <button style={{background:theme.background,color:theme.foreground}}>hello world</button>
}
function Toolbar(){return <div><ThemeButton></ThemeButton></div>
}
function App(){return <ThemeContext.Provider value={themes.dark}><Toolbar></Toolbar></ThemeContext.Provider>
}
export default App

useReducer能代替redux吗

import React,{useReducer} from 'React'const initialState = {count:0}const reducer = (state,action) => {switch (action.type) {case 'increment':return {count:state.count+1}case 'decrement':return {count:state.count-1}default:return state}
}function App(){const [state,dispatch] = useReducer(reducer,initialState)return <div>count:{state.count}<button onClick={() => dispatch({type:'increment'})}>increment</button><button onClick={() => dispatch({type:'decrement'})}>decrement</button>
}
export default App

useReducer和redux的区别
useReducer是useState的代替方案,用于state复杂变化
useReducer是单个组件状态管理,组件通讯还需要props
redux是全局状态管理,多组件共享数据

使用useMemo做性能优化

import React,{useState} from 'React'//子组件,点击click按钮,count被修改,子组件虽然没有用count,但是也被更新渲染
function Child({useInfo}){console.log('Child render...',useInfo)return <div><p>This is Child {useInfo.name} {useInfo.age}</p></div>
}
//父组件
function App(){console.log('Parent  render...')const [count,setCount] = useState(0)const [name,setName] = useState('双越老师')const useInfo ={name,age:20}return <div><p>useMomo demo<button onClick={() => setCount(count +1)}>click</button></p><Child userInfo={userInfo}></Child ></div>
}
export default App

使用useMemo做性能优化

import React,{useState,memo,useMemo} from 'React'//子组件
// function Child({useInfo}){//  console.log('Child render...',useInfo)
//  return <div>
//      <p>This is Child {useInfo.name} {useInfo.age}</p>
//  </div>
// }
// 类似class PureComponent,对props进行浅层比较
//memo相当于PureComponent
const Child =memo(({useInfo}) => {console.log('Child render...',useInfo)return <div><p>This is Child {useInfo.name} {useInfo.age}</p></div>
})
//父组件
function App(){console.log('Parent  render...')const [count,setCount] = useState(0)const [name,setName] = useState('双越老师')//const useInfo ={name,age:20}//用useMemo缓存数据,有依赖,name变化时缓存失效const userInfo = useMemo(() => {return {name,age:21}},[name])return <div><p>useMomo demo<button onClick={() => setCount(count +1)}>click</button></p><Child userInfo={userInfo}></Child ></div>
}
export default App

useMemo使用总结
React默认会更新所有子组件
class组件使用SCU和PureComponent做优化
Hooks中使用useMemo,但优化的原理是相同的

使用useCallback做性能优化

import React,{useState,memo,useMemo,useCallback} from 'React'//子组件
//memo相当于PureComponent
const Child =memo(({useInfo,onChange}) => {console.log('Child render...',useInfo)return <div><p>This is Child {useInfo.name} {useInfo.age}</p><input onChange={onChange}></input></div>
})
//父组件
function App(){console.log('Parent  render...')const [count,setCount] = useState(0)const [name,setName] = useState('双越老师')//const useInfo ={name,age:20}//用useMemo缓存数据,有依赖const userInfo = useMemo(() => {return {name,age:21}},[name])//加了onChange后父组件改变子组件又开始渲染//function onChange(e){//   console.log(e.target.value)//}//用useCallback缓存函数const onChange = useCallback(e =>{console.log(e.target.value)},[])return <div><p>useMomo demo<button onClick={() => setCount(count +1)}>click</button></p><Child userInfo={userInfo} onChange={onChange}></Child ></div>
}
export default App

useCallback使用总结
useMemo缓存数据
useCallback缓存函数
两者是React Hooks的常见优化策略

什么是自定义Hook

自定义Hook
封装通用的功能
开发和使用第三方Hooks
自定义Hook带来了无限的扩展性,解耦代码
代码演示useAxios

import React,{useState,useEffect} from 'React'
import axios from 'axios'//封装axios 发送网络请求的自定义Hook
function useAxios(url){const [loading,setLoading] = useState(false)const [data,setData] = useState()const [error,setError] = useState()useEffect(() =>{//利用axios发送网络请求setLoading(true)axios.get(url) //发送一个get请求.then(res => setData(res)).catch(err => setError(err)).finally(() => setLoading(false))},[url])return [loading,data,error]
}
export default useAxios//使用
import React from 'React'
import useAxios from './useAxios'
function App(){const url = 'http://localhost:8090/#/proapplyDetail?id=5d0e34a8711ebae8b9481a73a433f405'//数组解构const [loading,data,error] = useAxios(url)if(loading) return <div>loading...</div>return error? <div>{JSON.stringify(error)}</div>: <div>{JSON.stringify(data)}</div>
}
export default App

总结
本质是一个函数,以use开头(重要)
内部正常使用useState useEffect获取其他Hooks
自定义返回结果,格式不限
第三方Hooks
https://nikgraf.github.io/react-hooks/
https://github.com/umijs/hooks/

使用Hooks的两条重要规则

Hooks使用规范
再次强调命名规范useXxx
Hooks使用规范,重要!
关于Hooks的调用顺序
只能用于React函数组件和自定义Hook中,其他地方不可以
只能用于顶层代码,不能在循环、判断中使用Hooks
eslint插件 eslint-plugin-react-hooks可以帮到你

为何Hooks要依赖于调用顺序

关于Hooks调用顺序代码演示

import React,{useState,useEffect} from 'react'
function Teach({couseName}){//函数组件,纯函数,执行完即销毁//所以,无论组件初始化(render)还是组件更新(re-render)//都会重新执行一次这个函数,获取最新的组件//这一点和class组件不一样//render:初始化state的值 '张三'//re-render:读取state的值 '张三'const [studentName,setSudentName] = useState('张三')//render:初始化state的值 '双越'//re-render:读取state的值 '双越'const [teacherName,seTeacherName] = useState('双越')//if(couseName){//const [teacherName,seTeacherName] = useState('双越')//}//if(couseName){//useEffect(() => {模拟学生签到//localStorage.setItem('name',studentName)//})//}//render:添加effect函数//re-render:替换effect函数(内部的函数也会重新定义)useEffect(() => {//模拟学生签到localStorage.setItem('name',studentName)})//render:添加effect函数//re-render:替换effect函数(内部的函数也会重新定义)useEffect(() => {//开始上课console.log(`${teacherName} 开始上课,学生${studentName}`})return <div>课程:{couseName}讲师:{teacherName}学生:{studentName}</div>
}
export default Teach

Hooks调用顺序必须保持一致
无论是render还是re-render,Hooks调用顺序必须一致
如果Hooks出现在循环、判断里,则无法保证顺序一致
Hooks严重依赖于调用顺序!重要

class组件逻辑复用有哪些问题

React Hooks组件逻辑复用
回顾class组件的逻辑复用
使用Hooks做组件逻辑复用

class组件逻辑复用
Mixins早已废弃
高阶组件HOC
Render Prop

Mixins的问题
变量作用域来源不清
属性重名
Mixins引入过多会导致顺序冲突

高阶组件HOC
组件层级嵌套过多,不易渲染,不易调试
HOC会劫持props,必须严格规范,容易出现疏漏

Render Prop
学习本高,不易理解
只能传递纯函数,而默认情况下纯函数功能有限

Hooks组件逻辑复用有哪些好处

使用React Hooks做组件逻辑复用代码演示

Hooks做组件逻辑复用的好处
完全符合Hooks原有规则,没有其他要求,易理解记忆
变量作用域明确
不会产生组件嵌套

Hooks使用中的几个注意事项

React Hooks注意事项
useState初始化值,只有第一次有效
useEffect内部不能修改state
useEffect可能出现死循环

useState初始化值,只有第一次有效

import React,{useState} from 'react'
//子组件
function Child({userInfo}){//render:初始化state//re-render:只恢复初始化的state值,不会再重新设置新的值,只能用setName修改const [name,setName] = useState(userInfo.name)return <div><p>Child,props name:{userInfo.name}<p>Child,state name:{name}</div>
}
function App(){const [name,setName] = useState('双越')const userInfo = {name}return <div><div>Parent &nbsp;<button onClick={() => setName('慕课网')>setName</button></div><Child userInfo={userInfo} /></div>
}
export default App

点击setName按钮后

useEffect内部不能修改state

import React,{useState,useRef,useEffect} from 'react'
function UseEffectChangeState(){const [count,setCount] = useState(0)//模拟DidMountuseEffect(() => {console.log('useEffect...',count)//定时任务const timer = setInterval(() => {console.log('setInterval...',count)setCount(count+1)},1000)//清除定时任务return () => clearTimeout(timer)},[])//依赖为[]//依赖为[]时:re-render不会重新执行effect函数//没有依赖:re-render会重新执行effect函数return <div>count: {count}</div>
}
export default UseEffectChangeState


解决方法一:

import React,{useState,useRef,useEffect} from 'react'
function UseEffectChangeState(){const [count,setCount] = useState(0)//模拟DidMountlet myCount = 0useEffect(() => {console.log('useEffect...',count)//定时任务const timer = setInterval(() => {console.log('setInterval...',myCount )setCount(++myCount )//打破了纯函数的规则},1000)//清除定时任务return () => clearTimeout(timer)},[])//依赖为[]//依赖为[]时:re-render不会重新执行effect函数//没有依赖:re-render会重新执行effect函数return <div>count: {count}</div>
}
export default UseEffectChangeState


解决方法二:

import React,{useState,useRef,useEffect} from 'react'
function UseEffectChangeState(){const [count,setCount] = useState(0)//模拟DidMountconst countRef = useRef(0)useEffect(() => {console.log('useEffect...',count)//定时任务const timer = setInterval(() => {console.log('setInterval...',countRef.current)setCount(++countRef.current)//打破了纯函数的规则},1000)//清除定时任务return () => clearTimeout(timer)},[])//依赖为[]//依赖为[]时:re-render不会重新执行effect函数//没有依赖:re-render会重新执行effect函数return <div>count: {count}</div>
}
export default UseEffectChangeState

useEffect可能出现死循环

import React,{useState,useEffect} from 'React'
import axios from 'axios'//封装axios 发送网络请求的自定义Hook
function useAxios(url,config={}){const [loading,setLoading] = useState(false)const [data,setData] = useState()const [error,setError] = useState()useEffect(() =>{//利用axios发送网络请求setLoading(true)axios.get(url,config) //发送一个get请求.then(res => setData(res)).catch(err => setError(err)).finally(() => setLoading(false))},[url,config]) //依赖里有{}、[]引用类型//解决办法:将config进行解构return [loading,data,error]
}
export default useAxios

Hooks面试题解答

本章的主要内容
State Hook
Effect Hook
其他Hook
自定义Hook
组件逻辑复用
规范和注意事项

为什么要使用Hooks
完善函数组件的能力,函数更适合React组件
组件逻辑复用,Hooks表现更好
class复杂组件正在变得费解,不易拆解,不易测试,逻辑混乱

class组件中,相同的逻辑散落在各处
DidMount和DidUpdate中获取数据
DidMount绑定事件,WillUnMount解绑事件
使用Hooks,相同逻辑课分割到一个一个的useEffect中

React Hooks模拟组件生命周期
模拟componentDidMount - useEffect 依赖[]
模拟componentDidUpdate - useEffect无依赖,或者依赖[a,b]
模拟componentWillUnMount - useEffect中返回一个函数

useEffect中返回函数fn
useEffect依赖[],组件销毁时执行fn,等于WillUnMount
useEffect无依赖或依赖[a,b],组件更新时执行fn
即,下一次执行useEffect之前,就会执行fn,无论更新或卸载

如何自定义Hook

React Hooks性能优化
useMemo缓存数据
useCallback缓存函数
相当于class组件的SCU和PureComponent

React Hooks遇到哪些坑?
useState初始化值,只有第一次有效
useEffect内部,不能修改state
useEffect依赖引用类型,会出现死循环

React Hooks做组件逻辑复用的优点
完全符合Hooks原有规则,没有其他要求,易理解记忆
变量作用域明确
不会产生组件嵌套

(十三)react hooks相关推荐

  1. react hooks使用_为什么要使用React Hooks?

    react hooks使用 The first thing you should do whenever you're about to learn something new is ask your ...

  2. react hooks使用_如何使用React和Hooks检测外部点击

    react hooks使用 by Andrei Cacio 通过安德烈·卡西奥(Andrei Cacio) 如何使用React和Hooks检测外部点击 (How to detect an outsid ...

  3. react hooks使用_如何使用Hooks将React类组件转换为功能组件

    react hooks使用 by Balaganesh Damodaran 通过Balaganesh Damodaran 如何使用Hooks将React类组件转换为功能组件 (How to conve ...

  4. 探React Hooks

    前言 众所周知,hooks在 React@16.8 中已经正式发布了.而下周周会,我们团队有个同学将会仔细介绍分享一下hooks.最近网上呢有不少hooks的文章,这不免激起了我自己的好奇心,想先行探 ...

  5. 使用React Hooks你可能会忽视的作用域问题

    前言 其实React Hooks已经推出来一段时间了,直到前一阵子才去尝试了下,看到的一些博客都是以API的使用居多,还有一些是对于原理的解析.而我这篇文章想写的是关于React Hooks使用中的作 ...

  6. dw按钮图片滚动js_使用 React Hooks 实现仿石墨的图片预览插件(巨详细)

    点击上方"前端教程",选择"星标" 每天前端开发干货第一时间送达! 作者:DARRELL https://juejin.im/post/5e9bf299f265 ...

  7. 通过 React Hooks 声明式地使用 setInterval

    2019独角兽企业重金招聘Python工程师标准>>> 本文由云+社区发表 作者:Dan Abramov 接触 React Hooks 一定时间的你,也许会碰到一个神奇的问题: se ...

  8. mounty不可重新挂载因为先前没有完全卸载_【译】React Hooks测试完全指南

    原文地址:https://www.toptal.com/react/testing-react-hooks-tutorial 2018年底,React在16.8版本中引入了Hooks.它们(译注:指R ...

  9. 【译】什么是React Hooks

    原文:What are React Hooks? 作者:Robin Wieruch 译者:博轩 React Hooks 于 2018年10月的React Conf 中引入,作为在 React 函数组件 ...

最新文章

  1. 为什么科技互联网公司越来越重视数学?贾扬清等大牛如是说
  2. java 类加载过程
  3. SD 模块的几个增强
  4. Loadrunner-web资源相关图表
  5. Django中的Model模型
  6. 漫步凸分析三——凸集代数
  7. 我的一篇思想汇报——君子务本,本立而道生
  8. 第1章 编程心理门槛
  9. 编译 Linux2.6 内核总结【ZT】
  10. CSS 控件适配器的菜单样式解释
  11. WSUS全攻略之二 :安装WSUS
  12. mysql算法优化原则_Mysql语句优化的原则——让你写sql更加顺手
  13. 软件项目管理实用教程(人民邮电出版)第二章课后习题
  14. input type=file标签图片上传路径fakepath问题
  15. 新计算机c盘太小,Windows自带C盘扩容方法,c盘太小怎么重新分区
  16. 央视解说之韩乔生巅峰之作--夏普
  17. 苹果开发者账号和邓白氏编码申请总结
  18. 核心业务8:提现+展示还款信息和回款信息
  19. Pycharm Debugger - Frames Not Available
  20. QQ2010去除迷你首页O(∩_∩)O

热门文章

  1. python解决https私密连接警告信息
  2. 特征分解与奇异值分解
  3. mvn spring-boot:run 增加jvm启动参数
  4. MySql批量更新死锁案例分析
  5. 互联网广告系统综述一生态圈
  6. js验证input输入框(字母,数字,符号,中文)
  7. ES6 系列之 Babel 是如何编译 Class 的(上)
  8. Play! Framework 系列(四):DI 模式比较
  9. WCF中的Dispose
  10. 二叉树前序、中序和后序遍历的非递归实现