- 为什么会有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} = propsreturn <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.propsreturn <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 发送网络请求的自定义 Hookfunction 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 可能出现死循环
