uploadhub的技术博客 uploadhub的技术博客
首页
  • 学习笔记

    • 《HTML5和CSS3篇》
    • 《JavaScript基础篇》
    • 《JavaScript高级篇》
    • 《Ajax篇》
    • 《JavaScript模块化篇》
    • 《Node.js篇》
    • 《MongoDB篇》
    • 《Promise篇》
    • 《Git篇》
  • 《Vue2+Vue3篇》
  • 《React篇》
  • 一面-基础
  • 二三面-进阶
关于我
  • 分类
  • 标签
  • 归档

uploadhub

首页
  • 学习笔记

    • 《HTML5和CSS3篇》
    • 《JavaScript基础篇》
    • 《JavaScript高级篇》
    • 《Ajax篇》
    • 《JavaScript模块化篇》
    • 《Node.js篇》
    • 《MongoDB篇》
    • 《Promise篇》
    • 《Git篇》
  • 《Vue2+Vue3篇》
  • 《React篇》
  • 一面-基础
  • 二三面-进阶
关于我
  • 分类
  • 标签
  • 归档
  • Vue相关

  • React相关

    • 1.React基本使用
    • 2.React原理
    • 3.React面试真题演练
    • 4.React Hooks
      • 1.出几道react hooks面试题
        • 1.关于React Hooks
        • 2.面试问到React Hooks
        • 3.本章的主要内容
        • 4.面试题
      • 2.class组件存在哪些问题
        • 2.1 回顾React函数组件
        • 2.2 函数组件的特点
        • 2.3 class组件的问题
        • 2.4 React组件更易用函数表达
      • 3.用useState实现state和setState功能
        • 3.1 让函数组件实现state和setState
        • 3.2 useState使用总结
        • 3.3 Hooks命名规范
      • 4.用useEffect模拟组件生命周期
        • 4.1让函数组件模拟生命周期
        • 4.2 useEffect使用总结
        • 4.3 useEffect让纯函数有了副作用
      • 5.用useEffect模拟componentWillUnMount时的注意事项
        • 5.1 useEffect返回的函数,模拟componentWillUnMount,但不完全相等
        • 5.1 useEffect中返回函数fn
        • 5.2 小结
      • 6.useRef和useContext
      • 7.useReducer能代替Redux吗
        • 7.1 useReducer和redux的区别
      • 8.使用useMemo做性能优化
        • 8.1使用useMemo做性能优化
        • 8.2 useMemo使用总结
      • 9.使用useCallback做性能优化
        • 9.1 useCallback使用总结
      • 10.什么是自定义Hook
        • 10.1 总结
        • 参考链接-第三方Hooks
      • 11.使用Hooks的两条重要规则
        • 11.1 Hooks使用规范
        • 11.2 关于Hooks的调用顺序
      • 12.为何Hooks要依赖于调用顺序
      • 13.class组件逻辑复用有哪些问题
        • 13.1 Mixins的问题
        • 13.2 高阶组件HOC
        • 13.3 Render Prop
      • 14.Hooks组件逻辑复用有哪些好处
      • 15.Hooks使用中的几个注意事项
        • 15.1 useState初始化值,只有第一次有效
        • 15.2 useEffect内部不能修改state
        • 15.2.1 解决方法一:外部自定义变量,不用useState创建的State,不推荐
        • 15.2.2 解决方法二:使用Ref,推荐
        • 15.3 useEffect可能出现死循环(依赖里有{}、[]等引用类型)
        • 15.3.1 解决方法:解构对应引用类型的变量,再分别进行依赖
        • 15.3.2 出现该现象的原因
      • 16.Hooks面试题解答
        • 16.1 为什么要使用Hooks
        • 16.2 class组件中,相同的逻辑散落在各处
        • 16.3 React Hooks模拟组件生命周期
        • 16.4 useEffect中返回函数fn
        • 16.5 如何自定义Hook
        • 16.6 React Hooks性能优化
        • 16.7 React Hooks遇到哪些坑?
        • 16.8 React Hooks做组件逻辑复用的优点
  • Webpack和Babel

  • 项目设计
  • 项目流程
  • 二三面-进阶
  • React相关
uploadhub
2022-05-28
目录

4.React Hooks

# 1.出几道react hooks面试题

# 1.关于React Hooks

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

# 2.面试问到React Hooks

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

# 3.本章的主要内容

  1. State Hook
  2. Effect Hook
  3. 其他 Hook
  4. 自定义Hook
  5. 组件逻辑复用
  6. 规范和注意事项

# 4.面试题

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

# 2.class组件存在哪些问题

# 2.1 回顾React函数组件

//class 组件
class List extends React.Component { 
    constructor(props) {
		super(props) 
    }
	render() {
		const { list } = this.props
		return <ul>{list.map((item, index){ 
            return <li key={item.id}>
				<span>{item.title}</span>
			</li> 
        })}</ul>
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//函数组件
function List(props){
    const { list } = this.props
    return <ul>{list.map((item, index){ 
        return <li key={item.id}>
            <span>{item.title}</span>
        </li> 
    })}</ul>
}
1
2
3
4
5
6
7
8
9

# 2.2 函数组件的特点

  1. 没有组件实例
  2. 没有生命周期
  3. 没有state和setState,只能接收props

# 2.3 class组件的问题

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

# 2.4 React组件更易用函数表达

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

# 3.用useState实现state和setState功能

//示例代码
import React, { useState } from 'react' 

function ClickCounter(){
    //数组的解构
    const [count, setCount] = useState(0)
    
    // const arr = useState(0) 
    // const count = arr[0] 
    // const setCount = arr[1]
    
    return <div>
        <p>你点击了{count}次</p>
		<button onClick={ ()=> setCount(count +1)}> 点击 </button>
    <div>
}

export default ClickCounter 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 3.1 让函数组件实现state和setState

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

# 3.2 useState使用总结

  1. useState(initValue)传入初始值,返回数组[state,setState]
  2. 通过state获取值
  3. 通过setState(newValue)修改值

# 3.3 Hooks命名规范

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

# 4.用useEffect模拟组件生命周期

# 4.1让函数组件模拟生命周期

  1. 默认函数组件没有生命周期
  2. 函数组件是一个纯函数,执行完即销毁,自己无法实现生命周期
  3. 使用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)
	//返回一个函数
	//模拟WillUnMount
	return () => {
		window.clearInterval(timeId)
	}
},[])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 4.2 useEffect使用总结

  1. 模拟componentDidMount - useEffect 依赖[]
  2. 模拟componentDidUpdate - useEffect无依赖,或者部分依赖如[a,b]形式
  3. 模拟componentWillUnMount - useEffect中返回一个函数

# 4.3 useEffect让纯函数有了副作用

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

# 5.用useEffect模拟componentWillUnMount时的注意事项

# 5.1 useEffect返回的函数,模拟componentWillUnMount,但不完全相等

componentDidMount() {
    console.log(`开始监听 ${this.props.friendId} 在线状态`) 
}
componentWillUnMount(){
    console.log(`结束监听 ${this.props.friendId} 在线状态`) 
}
//friendId 更新
componentDidUpdate(prevProps) {
    console.log(`结束监听 ${prevProps.friendId} 在线状态`) 
    console.log(`开始监听 ${this.props.friendId} 在线状态`) 
}
1
2
3
4
5
6
7
8
9
10
11
function FriendStatus({ friendId }) {
	const [status, setStatus] = useState(false) 
    
    //模拟componentDidMount 和 componentDidUpdate
	useEffect(() => {
		console.log(`开始监听 ${friendId} 在线状态`)
        //【特别注意】
		//此处并不完全等同于 componentWillUnMount
		//props 发生变化,即更新,也会执行结束监听
		//准确的说:返回的函数,会在下一次 effect 执行之前,被执行
        return () => {
			console.log(`结束监听 ${friendId} 在线状态`)
        }
	})

	return <div>
		好友 {friendId} 在线状态:{status.toString()}
    </div>
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

这两段代码的效果是一样的,useEffect 返回的函数,执行效果要看useEffect本身的作用,如果useEffect本身即能模拟``componentDidMount又能模拟componentDidUpdate,那执行效果相当于componentWillUnMount和componentDidUpdate`都会执行

# 5.1 useEffect中返回函数fn

  1. useEffect第二个参数依赖空数组[],则只在组件销毁时执行返回的函数fn,等于componentWillUnMount
  2. useEffect无依赖(不传第二个参数)或部分依赖如[a,b],组件更新时也执行fn。即,下一次执行useEffect之前,就会执行fn,无论更新或卸载(相当于同时模拟了componentWillUnMount和componentDidUpdate两个生命周期)

# 5.2 小结

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

# 6.useRef和useContext

useRef

import React,{useRef,useEffect} from 'React'

function UseRef(){
  const btnRef = useRef(null) //初始值

  //不一定时获取DOM节点
  //const numRef = useRef(0)
  //numRef.current
  useEffect(() =>{
    console.log(btnRef.current) //DOM节点
  },[])
  return <div>
    <button ref={btnRef}>click</button>
  </div>
}
export default UseRef
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

useContext

/*使用示例*/
import React,{useContext} from 'React'

//主题颜色
const themes = {
	light:{
		foreground:'#000',
		background:'#eee'
	},
	dark:{
		foreground:'#fff
		background:'#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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 7.useReducer能代替Redux吗

# 7.1 useReducer和redux的区别

  1. useReducer是useState的代替方案,用于state复杂变化(并不能替代Redux)
  2. useReducer是单个组件状态管理,组件通讯还需要props
  3. redux是全局状态管理,多组件共享数据
/*useReducer使用示例*/
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 8.使用useMemo做性能优化

# 8.1使用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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 8.2 useMemo使用总结

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

# 9.使用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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 9.1 useCallback使用总结

  1. useMemo缓存数据
  2. useCallback缓存函数
  3. 两者是React Hooks的常见优化策略

# 10.什么是自定义Hook

  1. 封装通用的功能
  2. 开发和使用第三方Hooks
  3. 自定义Hook带来了无限的扩展性,解耦代码
//封装useAxios作为自定义Hook示例
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 10.1 总结

  1. 自定义Hook本质是一个函数,以use开头(重要)
  2. 内部正常使用useState useEffect获取其他Hooks
  3. 自定义返回结果,格式不限

# 参考链接-第三方Hooks

https://nikgraf.github.io/react-hooks/ https://github.com/umijs/hooks/

# 11.使用Hooks的两条重要规则

# 11.1 Hooks使用规范

  1. 再次强调命名规范useXxx
  2. Hooks使用规范,重要!

# 11.2 关于Hooks的调用顺序

  1. 只能用于React函数组件和自定义Hook中,其他地方不可以
  2. 只能用于顶层代码(不能跟在可能被打断的逻辑后面),不能在循环、判断中使用Hooks
  3. eslint插件 eslint-plugin-react-hooks可以帮到你
function Teach({ courseName }) {
	const [ studentName, setStudentName ] = useState('张三') 
    //第一种错误用法示范,判断中使用Hooks
    if (courseName == '') {
		useEffect(() => {
			localStorage.setItem('name', studentName) 
        })
    }
    //第二种错误用法示范,判断中使用Hooks
	for (let i = 0; i < 100; i++) {
		const [teacherName, setTeacherName] = useState('李四')
    }

    //第三种错误用法示范,在可能被打断的逻辑语句后面使用Hooks
    if (!courseName) { 
        return
    }
	useEffect(() => {
		console.log(`${teacherName} 开始讲课,学生是 ${studentName}`)
    })
    
	return <div>课程:{courseName},讲师:{teacherName},学生:{studentName} </div>
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

image-20221008201328030

# 12.为何Hooks要依赖于调用顺序

  1. 无论是render还是re-render,Hooks调用顺序必须一致
  2. 如果Hooks出现在循环、判断里,则无法保证顺序一致
  3. 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

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

  1. Mixins早已废弃
  2. class组件逻辑复用第一种方法:高阶组件HOC
  3. class组件逻辑复用第二种方法:Render Prop

# 13.1 Mixins的问题

  1. 变量作用域来源不清
  2. 属性重名
  3. Mixins引入过多会导致顺序冲突

# 13.2 高阶组件HOC

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

# 13.3 Render Prop

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

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

  1. 完全符合Hooks原有规则,没有其他要求,易理解记忆
  2. 变量作用域明确
  3. 不会产生组件嵌套

# 15.Hooks使用中的几个注意事项

  1. useState初始化值,只有第一次有效
  2. useEffect内部不能修改state
  3. useEffect可能出现死循环

# 15.1 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>
		<p>Child,state name:{name} </p>
	</div>
}
function App(){
	const [name,setName] = useState('uploadhub')
	const userInfo = {name}
	return <div>
		<div>
			Parent &nbsp;
			<button onClick={() => setName('uploadhub1')>setName</button>
		</div>
		<Child userInfo={userInfo} />
	</div>
}
export default App
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 15.2 useEffect内部不能修改state

/*演示依赖为空数组[]时,useEffect模拟componentDidMount,里面用到定时器等手段执行setXxxx来改变外层state,但state除了第一次执行后并不随定时器执行setXxx而发生变化的情况*/
import React,{useState,useRef,useEffect} from 'react'
function UseEffectChangeState(){
	const [count,setCount] = useState(0)
	//模拟componentDidMount
	useEffect(() => {
		console.log('useEffect...',count)
		
		//定时任务
		const timer = setInterval(() => {
			console.log('setInterval...',count)
			setCount(count+1)
		},1000)
		//清除定时任务
		return () => clearTimeout(timer)
	},[])//依赖为[]
	
	//依赖为[]时:re-render不会重新执行effect函数(指useEffect被传入的第一个参数),此时count一直在闭包里,保持不变
	//没有依赖:re-render会重新执行effect函数
	
	return <div>count: {count}</div>
}
export default UseEffectChangeState
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 15.2.1 解决方法一:外部自定义变量,不用useState创建的State,不推荐

import React,{useState,useRef,useEffect} from 'react'
function UseEffectChangeState(){
	const [count,setCount] = useState(0)
	//模拟componentDidMount
	let myCount = 0
	useEffect(() => {
		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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 15.2.2 解决方法二:使用Ref,推荐

import React,{useState,useRef,useEffect} from 'react'
function UseEffectChangeState(){
	const [count,setCount] = useState(0)
	//模拟componentDidMount
	const 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 15.3 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]) //依赖里有{}、[]等引用类型
	 return [loading,data,error]
}
export default useAxios
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 15.3.1 解决方法:解构对应引用类型的变量,再分别进行依赖

/*解决方法示例*/
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)
        //先解构赋值,再分别依赖
        const {a,b,c} = config
	    axios.get(url,config) //发送一个get请求
	    	.then(res => setData(res))
	    	.catch(err => setError(err))
	    	.finally(() => setLoading(false))
	 },[url,a,b,c]) 
	 return [loading,data,error]
}
export default useAxios
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 15.3.2 出现该现象的原因

  1. React使用Object.is()方法来监测比较useEffect的依赖是否发生改变,而且是一上来就比较一次(因为无论是依赖空数组[]还是部分依赖,useEffect都是先模拟componentDidMount,再判断是否模拟componentDidUpdate)。
  2. 这个语法对值类型影响不大,但对引用类型的比较却总返回false,而一开始就会比较一次,所以一旦useEffect的依赖中有引用类型的变量就会陷入死循环。

# 16.Hooks面试题解答

# 16.1 为什么要使用Hooks

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

# 16.2 class组件中,相同的逻辑散落在各处

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

# 16.3 React Hooks模拟组件生命周期

  1. 模拟componentDidMount - useEffect 依赖[]
  2. 模拟componentDidUpdate - useEffect无依赖,或者部分依赖如[a,b]形式
  3. 模拟componentWillUnMount - useEffect中返回一个函数

# 16.4 useEffect中返回函数fn

  1. useEffect依赖[],组件销毁时执行fn,等于WillUnMount
  2. useEffect无依赖或依赖[a,b],组件更新时执行fn
  3. 即,下一次执行useEffect之前,就会执行fn,无论更新或卸载

# 16.5 如何自定义Hook

请参照第10小节

# 16.6 React Hooks性能优化

  1. useMemo缓存数据
  2. useCallback缓存函数
  3. 相当于class组件的SCU和PureComponent

# 16.7 React Hooks遇到哪些坑?

  1. useState初始化值,只有第一次有效
  2. useEffect内部,不能修改state
  3. useEffect依赖引用类型,会出现死循环

# 16.8 React Hooks做组件逻辑复用的优点

  1. 完全符合Hooks原有规则,没有其他要求,易理解记忆
  2. 变量作用域明确
  3. 不会产生组件嵌套
#面试#React
3.React面试真题演练
1.Webpack 基本配置

← 3.React面试真题演练 1.Webpack 基本配置→

最近更新
01
HTTP协议及缓存机制
05-28
02
开发环境
05-28
03
JS基础知识(一)-变量类型和计算
05-28
更多文章>
Theme by Vdoing | Copyright © 2021-2023 uploadhub | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式