react面经

245 阅读6分钟

1.react-hooks解决的问题

  1. 函数组件中不能拥有自己的状态(state).在hooks之前函数组件是无状态的,都是通过props来获取父组件的状态,但是hooks提供了useState来维护组件内部的状态。

  2. 函数组件中不能监听组件的生命周期。useEffect聚合了多个生命周期函数。

  3. class组件中生命周期较为复杂。

  4. class组件逻辑难以复用(HOC,render props)

    class的弊端:你必须理解JavaScript中的this的工作方式,这与其他语言存在巨大的差异,代码也非常冗余,class也不能很好的压缩,并且会使热重载出现不稳定的情况。

    Hook 使你在非 class 的情况下可以使用更多的 React 特性

2. hooks对比 class的好处

  1. 写法更加的简洁
  2. 业务代码更加的聚合,让代码更加的居中好记,方便我们管理
  3. 逻辑复用方便

3. react函数式组件通信方式

  1. 父传子:使用props
  2. 子传父:也是使用props,不过父组件上绑定的是回调函数
  3. 跨组件之间的通信使用creatContext,useConftext

4. hooks 常用的API使用

  1. useState

    const [value, setValue] = useState(0)

    数组的第一个值是声明的状态,第二个值是状态的改变函数

    1. setState是同步的还是异步的(具体原因请点击这里

      • react18 版本之前,setState在同步环境下异步执行,异步环境下同步执行。

      • react18版本之后,无论是在同步环境还是异步环境中,setState都是异步的。

        setSate之所以会设置成异步是为了合并短时间内的多次渲染。

  2. useEffect

    useEffect接受一个回调函数以及依赖项,当依赖项发生变化时才会执行里面的回调函数。useEffect类似与class组件的didMount,didUpdata,willUnmount的生命周期

    注意点

    • useEffect是一步的在组件渲染完成后才会执行
    • useEffect的回调函数只能返回一个清除作用的处理函数或者不返回
    • 如果use Effect传入的依赖项是空数组那么useEffect内部的函数只会执行一次
  3. useMemo, useCallback

    useMemo和useCallback主要用于减少组件的更新次数,优化组件性能的。

    • useMemo接受一个回调函数以及依赖项,只有依赖项变化时才会重新执行回调函数。优化针对于当前组件高开销的计算

    useMome总结:(详细查看:useMome)

    1. useMome 是用来缓存计算属性的,它会在发现依赖未改变的情况下返回旧的计算属性值的地址。

    2. useMomo绝不是用的越多越好,缓存这项技术本身也需要成本。

    3. 使用场景:

      • 只需要给用巨大计算量的计算属性缓存即可,小的计算量就不需要了
      • 当计算属性被传入子组件,并且子组件使用了react.memo进行了缓存的时候,为了避免子组件不必要的渲染时使用。

    useCallback总结:(详细查看:useCallback

    1. useCallBack接受一个回调函数以及依赖项 ,并且返回该回调函数的memorize版本,只有依赖项重新变化时才会重新新的momorize版本,不变的时候返回的回调函数是同一个地址引用。优化针对于子组件渲染。useCallBack

    2. 注意:

      • useCallBack不要每个函数都包一下,否则就会变成反向优化,useCallback本身就是需要一定的性能的。
      • useCallBack并不能阻止函数重新创建,它只能通过依赖决定返回新的函数还是旧的函数,从而在依赖不变的情况下保证函数地址不变。
      • useCallBack需要配置React.memo使用。
    3. 使用场景:

      • 在往子组件传入一个函数并且子组件被React.memo缓存了的时候使用。
  4. useRef

    useRef类似与react.createRef

    const node = useRef(null)
    <input ref={node}/>
    

    这样可以通过node.current属性访问到该Dom元素。

    注意:useRef创建的对象在组件的整个生命周期内保持不变,也就是说每次重新渲染组件时,返回的ref对象都是同一个(使用React.createRef,每次重新渲染组件都会重新创建ref)

  5. useReducer

    useReducer类似与redux中的reducer

    useReducer传入一个计算函数和初始化state,类似与redux,通过返回的state我们可以访问状态,通过dispatch可以对状态做修改

    const initstate = 0;
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return {number: state.number + 1};
        case 'decrement':
          return {number: state.number - 1};
        default:
          throw new Error();
      }
    }
    function Counter(){
        const [state, dispatch] = useReducer(reducer, initstate);
        return (
            <>
              Count: {state.number}
              <button onClick={() => dispatch({type: 'increment'})}>+</button>
              <button onClick={() => dispatch({type: 'decrement'})}>-</button>
            </>
        )
    }
    ​
    

    6. useContext

    通过useContext我们可以更加方便的获取上层组件提供的context。主要是跨代组件之间的传值

    父组件

    import React, { createContext, Children } from 'react'
    import Child from './child'export const MyContext = createContext()
    ​
    export default function Parent() {
    ​
      return (
        <div>
          <p>Parent</p>
          <MyContext.Provider value={{name: 'cc', age: 21}}>
            <Child />
          </MyContext.Provider>
        </div>
      )
    }
    复制代码
    

    子组件

    import React, { useContext } from 'react'
    import { MyContext } from './parent'export default function Parent() {
      const data = useContext(MyContext) // 获取父组件提供的context
      console.log(data)
      return (
        <div>
          <p>Child</p>
        </div>
      )
    }
    复制代码
    

    使用步骤

    • 父组件创建并导出context:export const MyContext = createContext()
    • 父组件使用providervalue提供值:<MyContext.provide value={{name: 'cc', age: 22}} />
    • 子组件导入父组件的context:import { MyContext } from './parent'
    • 获取父组件提供的值:const data = useContext(MyContext)

    不过在多数情况下我们都不建议使用context,因为会增加组件的耦合性。

    7、useLayoutEffect

    useEffect,useLayoutEffect的区别:(具体解释请点击

    • useEffect 在全部渲染完毕后才会执行,是异步执行的。
    • useLayoutEffect 会在 Dom创建之后,视图渲染之前执行,并且会阻塞DOM;useLayoutEffect是同步的。

    useEffect的使用场景:

    • 一般使用useEffect,对Dom的初始化创建的操作时,使用useLayoutEffect.

    useLayoutEffect和componentDidMount事等价的。

    5. react 生命周期总结

    1. constructor()

      • 作用:在React组件挂载之前,会调用他的构造函数。

        注意:在为 React.Component 子类实现构造函数时,应在其他语句之前调用 super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。

      • 使用场景(只用这两种场景):

        1. 通过给this.state赋值对象来初始化内部的state

        2. 为事件处理函数绑定实例。

          注意:只能在构造函数中直接为 this.state 赋值。如需在其他方法中赋值,你应使用 this.setState() 替代。

    2. componentDidMount()

      • 作用:会在组件挂载后(插入DOM中)立即调用。

      • 使用场景:

        1. 依赖于DOM节点的初始化应该放在这里,如网络请求数据
        2. 添加订阅的好地方,比如redux需要添加订阅(注意:不要忘了在componentDidMount()中取消订阅)
    3. componentDidUpdate()

      • 作用:会在更新后立即调用,首次渲染不会调用。(如果shouldCompontentUpdate()返回false,则不会调用)

      • 使用场景:

        1. props传过来的值,更新前后值不一样,发送网络请求。
    4. componentWillUnmount()

      • 作用:会在组件卸载及销毁之前调用。此方法中执行必要的清理操作。

      • 使用场景:

        1. 清除timer
        2. 清除创建的订阅