文章目录
1、class 组件的问题?
- 大型组件很难拆分和重构,难测试
- 相同业务逻辑,分散到各个方法中,逻辑比较混乱
- 复用逻辑变得复杂,如Minxins 、HOC、Render Prop
所以react 提倡函数式编程,因为函数更灵活、更易拆分、更容易测试,但是函数太简单,但是函数组件加上hooks就能实现class 里的功能。
2、 useEffect 模拟组件的生命周期?
- 模拟componentDidMount 和componentDidUpdate
useEffect(()=>{
// 这是模拟的加载完和更新完的生命周期
})
- 模拟 componentDidMount
useEffect(()=>{
// 这是模拟的加载完,加载完了
},[])
- 模拟componentDidUpdate
useEffect(()=>{
// 这是模拟componentDidUpdate 更新完了
},[data1,data2])
- 模拟销毁的时候 componentWillUnmount
useEffect(()=>{
let timer = setInertval(()=>{
console.log('打印')
},1000)
// 模拟componentWillUnmount,就是return
// 只有当不传任何参数或依赖空数组,当props发生变化,也会执行销毁函数里的代码;也就是说销毁函数会在下一次执行effect之前执行
return ()=>{
clearInterval(timer)
}
},[])
useEffect 让纯函数有了副作用
1、useEffect 模拟componentWillUnMount 生命周期的注意事项
- useEffect 依赖于[](空数组)的时候,组件销毁就会执行return 返回的函数,就相当于组件销毁前componentWillUnMount生命周期
- useEffect 无依赖或依赖【a,b】,组件更新时也会执行return返回的函数,即 不过获取的值是上一次执行useEffect 的a,b的值。也就是return 的函数会在下一次useEffect执行之前执行
3、useRef 获取dom节点
4、useContext 组件之间共享状态,只存在于Context包裹下的所有子孙组件
父组件:
const houseDetailsData:HouseDetailsData = {
basic:{jyrxx:[], glrxx:[],fyzpxx:[]},
record:[]
}
// 创建一个context ,给他一个初始值
export const HouseDetailsContext = React.createContext(houseDetailsData)
{/**使用刚才命名的HouseDetailsContext.Provider 包裹你需要传给的子孙组件,把值传进去之后,他包裹的所有组件都可以使用houseDetailsInfo值 */}
<HouseDetailsContext.Provider value={houseDetailsInfo}>
<HouseDetails></HouseDetails>
</HouseDetailsContext.Provider>
然后子孙组件使用的话:
import React, { useContext } from 'react';
// 引入你刚才定义的Context 变量
import { HouseDetailsContext } from './index'
// 使用useContext就可以使用上级传下来的值
const houseDetailsData = useContext(HouseDetailsContext)
<div>{houseDetailsData.name}</div>
这样子就是一个useContext的基本使用
5、useReducer 和redux的区别?
- useReducer是useState的代替方案,用于state的复杂变化,借鉴了redux的实现方式
- useReducer是单个组件状态管理,组件通讯还是需要props
- redux 是全局状态管理,多组件共享数据
- 所以useReducer 和redux 不是一个东西,只不过useReducer借鉴了redux实现方式而已。
import React, { useReducer } from 'react'
const initialState = { count: 0 }
const reducer = (state, action) => {
switch (action) {
case 'add':
return { count: state.count + 1 }
case 'dele':
return { count: state.count - 1 }
default:
return state
}
}
const UseReducerDemo = () => {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<>
值:{state}
<button onClick={() => dispatch('add')}>增加</button>
<button onClick={() => dispatch('dele')}>减少</button>
</>
)
}
export default UseReducerDemo
6、使用useMemo 做性能优化
场景:当父组件更新,子组件依赖的属性并没有变化,不想子组件也更新,可用这个做优化
- 父组件更新所有的子组件默认更新
- class组件使用scu 和superComponent做优化
- hooks 中使用useMemo,原理都是相同的,都是对依赖的变量进行浅层对比
父组件使用useMemo包裹住你想判断的变量,这些变量改变的话,才会重新渲染子组件,使用useMemo缓存数据
import React, { useState, useMemo } from 'react'
import ChangeName from './ChangeName'
const UseReducerDemo = () => {
const [count, setCount] = useState(0)
const [name, setName] = useState('lisi')
// const useInfo = { name, age: 20 }
// name是我监听的变量,只有这个变量改变了我的字组件才会重新渲染
const useInfo = useMemo(() => {
return { name, age: 20 }
}, [name])
return (
<>
值:{count}
<button onClick={() => setCount(count + 1)}>增加</button>
<ChangeName info={useInfo} />
</>
)
}
export default UseReducerDemo
子组件就是用memo来包裹住整个子组件,写法如下:
import React, { memo } from 'react'
const ChangeName = memo((props) => {
const { info } = props
return (
<>
<div>名字:{info.name} --- 年龄:{info.age}</div>
</>
)
})
export default ChangeName
7、使用useCallback做性能优化
- useCallback用来缓存函数,需要跟memo来配合,如果父组件向子组件传入一个函数,不用useCallback来包裹,那么父组件更新,子组件一样会更新
- useMemo用来缓存数据
- 这两种就是react hook 常用性能优化方案
父组件使用useCallback
import React, { useState, useMemo, useCallback } from 'react'
import ChangeName from './ChangeName'
const UseReducerDemo = () => {
const [count, setCount] = useState(0)
const [name, setName] = useState('lisi')
// const useInfo = { name, age: 20 }
const useInfo = useMemo(() => {
return { name, age: 20 }
}, [name])
// 使用useCallback来缓存函数
const onchange = useCallback((e) => {
console.log('e', e.taget.value)
}, [])
return (
<>
值:{count}
<button onClick={() => setCount(count + 1)}>增加</button>
<ChangeName useInfo={useInfo} onchange={onchange} />
</>
)
}
export default UseReducerDemo
子组件接收
import React, { memo } from "react";
const ChangeName = memo((props) => {
const { useInfo, onchange } = props;
console.log(props);
return (
<>
<div>
名字:{useInfo.name} --- 年龄:{useInfo.age}
</div>
<input onChange={onchange} />
</>
);
});
export default ChangeName;
8、自定义hook
- 封装通用的功能
- 自定义hook带来了无限的扩展性,解耦代码
- 本质是一个函数,以use开头
- 自定义hook内部可以使用useState、useEffect或者其他的hooks
- 自定义返回结果,格式不限
场景:封装axios,useAxios
9、 使用hooks的两条重要规则
- useXxx 的命名规范
1、 hooks 的使用规范
- 只能用于react函数组件和自定义hooks中,其他地方不可以使用(class组件不能、函数不能)
- 只能用于顶层代码,不能在循环、判断中使用hooks
- eslint 插件 eslint-plugin-react-hooks帮助你
2、为何hooks 要依赖于调用顺序?
- 无论是组件初始化还是组件更新,hooks的调用顺序必须一致
- 如果hooks 中出现判断或者循环则无法保证顺序的一致。
- hooks 严重依赖于调用顺序
10 、class 组件逻辑复用的问题
-
高阶组件:
- 组件层级嵌套过多,不易渲染,不易调试
- HOC会劫持props,必须严格规范,容易出现疏漏
-
Render Props:
- 学习成本高,不易理解
- 只能传递纯函数,而默认情况下纯函数功能有限
11、使用react hooks 来进行组件逻辑复用有哪些好处
- 完全符合hooks原有规则,没有其他要求,易理解
- 变量作用域很明确
- 不会产生组件嵌套
12、react hooks 有哪些注意事项(坑)?
-
useState 初始化值,只有第一次有效。重新渲染时只恢复初始化的state值,不会再重新设置新的值,如果想要修改,只能使用setSate修改值;例如父组件向子组件传递一个属性,子组件初始化的useState是父组件的值,当父组件改变那个属性,子组件state不会发生改变
- -
useEffect 依赖为【】的时候,重新渲染不会执行effect 函数,所以在里面修改state值无效;如果useEffect 没有依赖,重新渲染会执行effect函数,修改state值有效,解决方案如下:
-
解决,在外层定义一个useRef
-
useEffect 可能会出现死循环,在useEffect 依赖是一个数组或者对象的时候,可能会出现死循环。解决办法就是将对象打散变成一个值类型,在里面进行重组
-
原因是useEffect用来判断依赖是否发生改变是用的object.is,,来进行判断,当是对象和数组时返回的都是false,不一样,所以一直会循环
在这里插入图片描述
13、react hooks 做组件复用的优点?
- 变量作用域明确
- 不会产生组件嵌套
- 完全符合hooks原有原则,没有其他要求,易理解记忆