- 为什么会有React Hooks, 他解决了哪些问题?
- React Hooks 如何模拟生命周期?
- 如何自定义Hook ?
- React Hooks 性能优化 ?
- 使用React Hooks 遇到哪些坑 ?
- Hooks 相比HOC 和Render Prop有哪些优点?
React Hooks, 是v16.8之后新增的API
说明:1. 可选功能(class组件 vs Hooks)2. 100%向后兼容, 没有破坏性改动 3.不会取代class组件, 尚无计划要移除class组件。
- State Hook
- Effect Hook
- 其他Hook
- 自定义Hook
- 组件逻辑复用
- 规范和注意事项
**
0⃣️、class组件存在哪些问题
// 函数式组件
function List(props) {
const {list} = props
return <ul>{list.map((item, index) => <li key={item.id}>
<span>{item.name}</span>
</li>)}</ul>
}
// class 组件
class List extends React.Component {
constructor(props) {
super(props)
}
render() {
const {list} = this.props
return <ul>{list.map((item, index) => <li key={item.id}>
<span>{item.name}</span>
</li>)}</ul>
}
}
class组件的问题:
- 大型组件很难拆分和重构、很难测试(即class不易拆分)
- 相同业务逻辑, 分散到各个中, 逻辑混乱
- 复用逻辑变的复杂, 如Mixins、HOC、Render prop
函数组件的特点:
- 函数组件是一个纯函数, 执行完即销毁
- 没有组件实例
- 默认没有state、setState,只接受porps。需要state hook,即把state功能“钩”到纯函数中。
- 默认没有生命周期。使用Effect hook把生命周期“钩”到纯函数中
函数组件三要素: 1. 引入React 2. 本身是函数 3. 返回jsx
1⃣️、用useState实现state和setState功能
useState()传入初始值, 返回[state, setState]; 通过state获取值、通过setState()修改值
import {useState} from 'react'
const [count, setCount] = useState(0) // 初始值
<p>{count}</p><button onClick={() => setCount(count+1)}></button>
Hooks命名规范:规定所有的hooks都用use开头; 自定义hook也以use开头; 非hook的地方, 尽量不要使用use开头的写法
2⃣️、用useEffect模拟生命周期
useEffect让纯函数有了副作用
- 第一个参数,函数
第二个参数,非必须,数组元素是依赖的state
import {useEffect} from 'react'
useEffect(() => {
// useEffect 模拟class组件的DidMount、Didupdate
})
useEffect(() => {
// useEffect 模拟class组件的DidMount
}, [])
useEffect(() => {
// useEffect 模拟class组件的Didupdate
}, [state])
模拟componentDidMount - useEffect依赖[]
- 模拟componentDiaUpdate - useEffect依赖、无依赖
- 模拟componentWillUnMount - useEffect 中返回一个函数
2.2 用useEffect模拟WillUnMount时注意事项
useEffect(() => {
return () => {
// 模拟willunMount
}
})
- useEffect 不完全等同于willUnMount;
- props发生变化, 即更新, 也会执行结束监听;
- 返回的函数, 会在下一次effect执行之前,被执行。
3⃣️、其他Hook
3.1 useRef
function comp () {
const btnRef = useRef(null) // 初始值
btnRef.current // DOM 节点
return <div ref={btnRef}></div>
}
3.2 useContent
const ThemeContext = React.createContext(themes.light) // 初始化
function Comp () {
const theme = useContext(ThemeContext)
return <div style={{theme}}></div>
}
function App() {
return <ThemeContext.Provide><Comp /></ThemeContext.Provider>
}
3.3 useReducer
useReducer能代替redux吗?
const initialState = {count: 0}
const reducer = (state, action) => {
swtich (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'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
</div>
}
- useReducer是useState的替代方案, 用于state复杂变化
- useReducer是单个组件状态管理, 组件通讯还需要props
- redux是全局的状态管理, 多组件共享数据
3.4 useMemo
- React 默认会更新所有子组件
- class组件使用SCU和React.PureComponent 做优化
Hooks 中使用useMemo, 但优化的原理是相同的。memo对函数组件进行封装; useMemo对数据进行包裹, 设置依赖的state。
const userInfo = useMemo(() => ({name, age: 12}), [name])
function Comp => memo(({ userInfo }) => {
return <div>{userInfo.name}-{userInfo}
<input onChange={onChange} /></div>
})
3.5 useCallback
React Hooks 常见优化策略:useMemo 缓存数据; useCallback 缓存函数const onChange = useCallback(e => { })
<Comp onChange={onChange}></Comp>
4⃣️、自定义Hook
封装通用的功能;开发和使用第三方Hooks(react-hooks);自定义Hook带来无限的扩展性,解耦代码// 封装 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).then(res => setData(res)).catch(err => setError(err))
.finally(() => setLoading(false))
}, [url])
return [loading, data, error]
}
function Comp() {
const [loading, data, error] = useAxios(url)
if (loading) return <div>loading</div>
return error ? <div>JSON.stringify(error)</div>
: <div>JSON.stringify(error)</div>
}
Hooks使用规范:
命名使用use开头
- 调用顺序。函数组件是纯函数, 执行完即销毁。无论是组件初始化(render)还是组件更新(re-render),都会执行一次函数, 获取最新组件。(eslint-plugin-react-hooks)
- 只能用于React函数组件和自定义Hook中
- 只能用于顶层代码, 不能打断,不能在循环、判断中使用Hooks
5⃣️、逻辑复用
clas组件逻辑复用有哪些问题?
- mixins(废弃)。变量作用域来源不清;属性重名; Mixins引入过多会导致顺序冲突
- 高阶组件HOC。组件嵌套过多, 不易渲染,不易调试; HOC劫持props,必须严格规范, 容易出现疏漏
- Render prop。不易理解; 只能传递纯函数, 而默认情况下纯函数功能有限
使用hooks实现逻辑复用 有哪些好处?
- 完全符合Hooks原有规则, 没有其他要求, 易于理解记忆
- 变量作用域和明确
不会产生组件嵌套
function useMousePosition () {
const [x, setX] = useState(0)
const [y, setY] = setState(0)
useEffect(() => {
function mouseHandler(event) {
setX(event.clientX)
setY(event.clientY)
document.body.addEventListenter('mousemove', mouseHandler)
return document.body.removeEventListener('mousemove', mouseHandler)
}
}, [])
}
function Comp () {
const [x, y] = useMousePosition()
return <div>{x},{y}</div> }
6⃣️、注意事项
useState 初始化值, 只有第一次有效
- useEffect 内部不能修改state
- useEffect 可能出现死循环