1. 为什么会有React Hooks, 他解决了哪些问题?
    2. React Hooks 如何模拟生命周期?
    3. 如何自定义Hook ?
    4. React Hooks 性能优化 ?
    5. 使用React Hooks 遇到哪些坑 ?
    6. 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组件存在哪些问题

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

    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()修改值

    1. import {useState} from 'react'
    2. const [count, setCount] = useState(0) // 初始值
    3. <p>{count}</p><button onClick={() => setCount(count+1)}></button>

    Hooks命名规范:规定所有的hooks都用use开头; 自定义hook也以use开头; 非hook的地方, 尽量不要使用use开头的写法
    2⃣️、用useEffect模拟生命周期
    useEffect让纯函数有了副作用

    • 第一个参数,函数
    • 第二个参数,非必须,数组元素是依赖的state

      1. import {useEffect} from 'react'
      2. useEffect(() => {
      3. // useEffect 模拟class组件的DidMount、Didupdate
      4. })
      5. useEffect(() => {
      6. // useEffect 模拟class组件的DidMount
      7. }, [])
      8. useEffect(() => {
      9. // useEffect 模拟class组件的Didupdate
      10. }, [state])
    • 模拟componentDidMount - useEffect依赖[]

    • 模拟componentDiaUpdate - useEffect依赖、无依赖
    • 模拟componentWillUnMount - useEffect 中返回一个函数

    2.2 用useEffect模拟WillUnMount时注意事项

    1. useEffect(() => {
    2. return () => {
    3. // 模拟willunMount
    4. }
    5. })
    • useEffect 不完全等同于willUnMount;
    • props发生变化, 即更新, 也会执行结束监听;
    • 返回的函数, 会在下一次effect执行之前,被执行。

    3⃣️、其他Hook
    3.1 useRef

    1. function comp () {
    2. const btnRef = useRef(null) // 初始值
    3. btnRef.current // DOM 节点
    4. return <div ref={btnRef}></div>
    5. }

    3.2 useContent

    1. const ThemeContext = React.createContext(themes.light) // 初始化
    2. function Comp () {
    3. const theme = useContext(ThemeContext)
    4. return <div style={{theme}}></div>
    5. }
    6. function App() {
    7. return <ThemeContext.Provide><Comp /></ThemeContext.Provider>
    8. }

    3.3 useReducer
    useReducer能代替redux吗?

    1. const initialState = {count: 0}
    2. const reducer = (state, action) => {
    3. swtich (action.type) {
    4. case 'increment':
    5. return { count: state.count + 1}
    6. case 'decrement':
    7. return { count: state.count - 1}
    8. default:
    9. return state
    10. }
    11. }
    12. function App() {
    13. const [state, dispatch] = useReducer(reducer, initialState)
    14. return <div>
    15. count: {state.count}
    16. <button onClick={() => dispatch({type: 'increment'})}>+</button>
    17. <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    18. </div>
    19. }
    • useReducer是useState的替代方案, 用于state复杂变化
    • useReducer是单个组件状态管理, 组件通讯还需要props
    • redux是全局的状态管理, 多组件共享数据

    3.4 useMemo

    • React 默认会更新所有子组件
    • class组件使用SCU和React.PureComponent 做优化
    • Hooks 中使用useMemo, 但优化的原理是相同的。memo对函数组件进行封装; useMemo对数据进行包裹, 设置依赖的state。

      1. const userInfo = useMemo(() => ({name, age: 12}), [name])
      2. function Comp => memo(({ userInfo }) => {
      3. return <div>{userInfo.name}-{userInfo}
      4. <input onChange={onChange} /></div>
      5. })

      3.5 useCallback
      React Hooks 常见优化策略:useMemo 缓存数据; useCallback 缓存函数

      1. const onChange = useCallback(e => { })
      2. <Comp onChange={onChange}></Comp>

      4⃣️、自定义Hook
      封装通用的功能;开发和使用第三方Hooks(react-hooks);自定义Hook带来无限的扩展性,解耦代码

      1. // 封装 axios 发送网络请求的自定义 Hook
      2. function useAxios(url) {
      3. const [loading, setLoading] = useState(false)
      4. const [data, setData] = useState()
      5. const [error, setError] = useState()
      6. useEffect(() => { /* 利用 axios 发送网络请求 */
      7. setLoading(true)
      8. axios.get(url).then(res => setData(res)).catch(err => setError(err))
      9. .finally(() => setLoading(false))
      10. }, [url])
      11. return [loading, data, error]
      12. }
      13. function Comp() {
      14. const [loading, data, error] = useAxios(url)
      15. if (loading) return <div>loading</div>
      16. return error ? <div>JSON.stringify(error)</div>
      17. : <div>JSON.stringify(error)</div>
      18. }

      Hooks使用规范:

    • 命名使用use开头

    • 调用顺序。函数组件是纯函数, 执行完即销毁。无论是组件初始化(render)还是组件更新(re-render),都会执行一次函数, 获取最新组件。(eslint-plugin-react-hooks)
      • 只能用于React函数组件和自定义Hook中
      • 只能用于顶层代码, 不能打断,不能在循环、判断中使用Hooks

    5⃣️、逻辑复用
    clas组件逻辑复用有哪些问题?

    • mixins(废弃)。变量作用域来源不清;属性重名; Mixins引入过多会导致顺序冲突
    • 高阶组件HOC。组件嵌套过多, 不易渲染,不易调试; HOC劫持props,必须严格规范, 容易出现疏漏
    • Render prop。不易理解; 只能传递纯函数, 而默认情况下纯函数功能有限

    使用hooks实现逻辑复用 有哪些好处?

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

      1. function useMousePosition () {
      2. const [x, setX] = useState(0)
      3. const [y, setY] = setState(0)
      4. useEffect(() => {
      5. function mouseHandler(event) {
      6. setX(event.clientX)
      7. setY(event.clientY)
      8. document.body.addEventListenter('mousemove', mouseHandler)
      9. return document.body.removeEventListener('mousemove', mouseHandler)
      10. }
      11. }, [])
      12. }
      13. function Comp () {
      14. const [x, y] = useMousePosition()
      15. return <div>{x},{y}</div> }

      6⃣️、注意事项

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

    • useEffect 内部不能修改state
    • useEffect 可能出现死循环