# React

# 基础知识

# 生命周期

图片来自这里 (opens new window)

getDerivedStateFromProps 一般只用于表单控件获取默认值,其它情形不推荐使用 (而是用计算属性替代)。

componentDidMountcomponentDisUpdate 阶段 DOM 已经渲染完成了,这时候可以执行带有副作用的操作。

getSnapshotBeforeUpdate 会在 render 之前被调用,用于获取 render 之前的 DOM 状态。它的返回值会被传递给 componentDidUpdate,用于在更新之后修改 DOM。

shouldComponentUpdate 用于性能优化,很多时候可以由 PureComponent 自动实现。

# 受控组件 vs. 非受控组件

对于 DOM 元素来说,加了 value 属性就是受控组件 (opens new window)<input type="text" value={msg} />。不加 value 属性就是非受控组件 (opens new window),这时可以用 Ref (opens new window) 访问非受控组件。

UI 组件库 (而非 DOM 元素),也有受控和非受控的概念。

# 函数式组件 vs. 类组件 vs. Hooks

类组件一直是最经典的写法,函数式组件不能执行副作用,Hooks 的出现让函数式组件能执行副作用。

# Hooks

Hooks 于 React Conf 2018 提出,可以更细粒度地复用代码逻辑,并解决高阶组件 (Higher Order Components, HOC) 的嵌套地狱 (wrapper hell) 问题 (如下图所示)。

Hooks 还可以解决类组件 (Class Components) 带来的巨型组件 (giant components) 问题,如下所示:

class FriendStatus extends React.component {
  render() { return ...; }

  componentDidMount() {
    this.subscribeToStore(this.props.friend.id)
    this.fetchFriendStatus(this.props.friend.id)
    this.startTimers()
  }

  componentWillUnmount() {
    // 我们很难发现这里是否有正确释放资源
    this.unsubscribeFromStore(this.props.friend.id)
    this.cancelPendingRequests()
    this.stopTimers()
  }
}

Hooks 的限制:不能在条件语句内使用 if (props.condition) { useState() }

# HOC vs. Hooks

高阶组件 (HOC) 是一个函数,输入一个 Component,返回另一个 Component,能够实现一些代码复用需求,类似装饰器模式。HOC 本身并不是 React 的一部分,只是一种高级的编码技巧。

下面是使用 HOC 的一个例子,思想就是使用函数 wrap 了一层,代码上还是很绕的,初学者需要适应一段时间。

使用 HOC 复用计数器代码
import React from 'react';
import ReactDOM from 'react-dom';

function withCounter(WrappedComponent) {
  return class extends React.Component {
    state = { count: 0 };

    handleDecrement = () => {
      this.setState({ count: this.state.count - 1 });
    };

    handleIncrement = () => {
      this.setState({ count: this.state.count + 1 });
    };

    render() {
      const { count } = this.state;

      return (
        <WrappedComponent {...this.props} count={count} onIncrease={this.handleIncrement} onDecrease={this.handleDecrement} />
      );
    }
  };
};

const App = ({ count, onIncrease, onDecrease }) => {
  return (
    <div>
      <div>Current count: {count}</div>
      <div>
        <button onClick={onDecrease}>-</button>
        <button onClick={onIncrease}>+</button>
      </div>
    </div>
  );
};

const AppWithCounter = withCounter(App);

ReactDOM.render(<AppWithCounter />, document.getElementById('root'));

下面是使用 Hooks 的一个例子,比起前面的 HOC 来说,要更加的简洁易懂。

使用 Hooks 复用计数器代码
import React, { useState } from 'react';
import ReactDOM from 'react-dom';

const useCounter = () => {
  const [count, setCount] = useState(0);

  const onIncrease = () => setCount(count + 1);
  const onDecrease = () => setCount(count - 1);

  return [ count, onIncrease, onDecrease ];
};

const App = () => {
  const [ count, onIncrease, onDecrease ] = useCounter();

  return (
    <div>
      <div>Current count: {count}</div>
      <div>
        <button onClick={onDecrease}>-</button>
        <button onClick={onIncrease}>+</button>
      </div>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));

# 在 React 中编写样式

三种方式:

  • CSS,就是 CSS 原生方式
  • CSS Modules
  • CSS in JS

CSS in JS 又有很多实现:

  • styled-components
  • redium
  • 等等

# TypeScript

这篇文章 (opens new window)介绍了如何标注 props.children 的类型。

# 状态管理

# Context

# Redux

# 参考资料

  • reactjs.org
  • medium 上的这篇文章 (opens new window) 提出了 HOC 带来的 wrapper hell 问题,介绍了 Hooks 如何解决这个问题。分别用两种技术实现了一个计数器逻辑复用的例子,并进行比较。
  • 《The Road to React》:https://github.com/the-road-to-learn-react/the-road-to-react-chinese
  • https://github.com/rwieruch/blog_robinwieruch_content