React相关知识点细化

439 阅读4分钟

0. 前言

前段时间,实习生面试发现自己对于React某些知识点不是很熟悉。因此,小编花了一天的时间整理了一下相关的知识点。这篇文章记录了小编的整理结果。这篇文章主要包含三个内容:React Hook、React 生命周期,以及React 函数组件和类组件的区别。

1. React Hook

React的官网对于Hook是这样介绍的:

Hook 是 React16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

在这篇文章的第一部分,我们就来看一看Hook相关的内容。

1.1 为什么要使用Hook?

  • 方便组件之间服用状态逻辑:在没有 Hook 时,React内有提供将可复用性行为“附加”到组建的途径。Hook使得在无需修改组件结构的情况下复用状态逻辑
  • 方便理解复杂组件:Hook将组件中相互关联的部分拆分成更小的函数,而并非强制按照生命周期来划分,可以使得组件的内部状态变得更加可预测。
  • 难以理解的class:在非 class 的情况下可以使用更多的 React 特性。Hook提供了解决函数组件中添加状态和调用其生命周期的问题。

1.2 常用的Hook整理

  1. useState():通过在函数组件中调用它来给组件添加一些内部 state. useState()会返回一对值:当前状态和让它更新的函数。它的用法类似于 class 组建的this.state().不同的是state只能声明一次,而Hook则可以多次使用。使用方法如下:
import React, { useState } from 'react';

function Example() {
  // 声明一个叫 "count" 的 state 变量  const [count, setCount] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  1. useEffect():是一个Effect Hook,给函数组件增加了操作副作用的能力。它跟class组件中的componentDidMountcomponentDidUpdate,以及componentWillUnmount具有相同的用途,只不过合并成了一个API。当你使用useEffect时,就是告诉React在完成DOM的更改后运行你的副作用函数。使用方法如下:
import React, { useState, useEffect } from 'react';
function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {    
      document.title = `You clicked ${count} times`;  
  });
  
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

这段代码的功能是在componentDidMount这个生命周期中改变文档的title属性。

  1. useContext():跨足间共享数据的钩子函数。这个钩子函数跟context这个属性相关。对于context属性而言,一定会有一个context的生产者和消费者(使用者)。生产者使用React.createContext()方法来创建context;而消费者使用useContext这个钩子函数来使用它。在类组件中,使用context属性,需要在contextType中传入需要使用到的context属性名称。函数组件的使用方法如下:
const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};

const ThemeContext = React.createContext(themes.light);

function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);  
  return (    
      <button style={{ background: theme.background, color: theme.foreground }}>
          I am styled by theme context!   
      </button>  
  );
}
  1. useReducer()useState的替代方案,类似于redux中的写法。使用方法如下:
const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}
  1. useRef():获取组件的实例。返回一个可变的ref对象。Ref时访问DOM节点或在render中创建React元素的方式。即在声明周期之外有第三方操作的处理工具。使用方法如下:
function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}
  1. useMemo()useCallback()useMemo缓存数据,useCallback缓存函数。

1.3 Hook 分类

可以将Hook分为三类:

  • 输入:useState,useContext,useReducer
  • 输出:useEffect,useMemo和useCallback
  • 其他:useRef

1.4 用Hook模拟React生命周期

  1. componentDidMount:第二个参数为空数组可以模拟componentDidMount
useEffect(() => {
    console.log('第一次渲染使用')
},[])
  1. componentDidUpdate:利用Ref创建表示是否是第一次调用的对象,然后进行判断。没有第二个参数表示监听所有属性;监听多个属性的变化需要将属性作为数组传入第二个参数。
const mounted = useRef()
useEffect(() => {
    if(!mounted) {
        mounted = true
    } else {
        console.log('组件更新使用')
    }    
})
  1. componentWillUnmount:组件卸载时需要清除Effect创建的订阅,定时器等资源。useEffect返回的函数可以表示组件已死亡。
useEffect(() => {
    return () => {
        console.log('组件已死亡')
    }
}, [])

2. React 生命周期

从广义上讲,分为三个阶段:挂载、渲染、卸载。具体如下:

image.png

其中重要的阶段有:

  • 挂载阶段:componentWillMount、componentDidMount,render
  • 运行阶段:componentWillUpdate、render、componentDidUpdate
  • 卸载阶段:componentWillUnmount

在React的新版本中取消了三个阶段,新增了两个阶段:

  • 取消的:componentWillMount、componentWillReceiveProps、componentWillUpdate
  • 新增的:getDerivedStateFromProps、getSnapshotBeforeUpdate

3. React FunctionComponent 和 ClassComponent 的区别

  • 类组件定义函数需要绑定this,函数组件不需要
  • 类组件有生命周期,函数组件没有
  • 类组件有状态state属性,并且可以根据这个属性进行条件渲染,函数组件没有
  • props传参的差异,函数组件的props是作为参数传递进来的,因此中间不能改变;而类组件的props是作为类的一个属性传递的,在执行时可以随时改变这个属性。