透过React typescript definition文件看React组件设计

分析时版本:

"react": "^16.13.1",
"@types/react": "^16.9.0",

先从React 实例看起

type ReactInstance = Component<any> | Element;

一个ReactInstance可以是React定义的组件(Component<any>)或者是lib dom定义的Element

// Base component for plain JS classes
// tslint:disable-next-line:no-empty-interface
interface Component<P = {}, S = {}, SS = any> extends ComponentLifecycle<P, S, SS> { }

可以看到Component继承自ComponentLifecycle,也就是我们经常会用到的生命周期接口了。

interface ComponentLifecycle<P, S, SS = any> extends NewLifecycle<P, S, SS>, DeprecatedLifecycle<P, S> {

        componentDidMount?(): void;

        shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean;

        componentWillUnmount?(): void;

        componentDidCatch?(error: Error, errorInfo: ErrorInfo): void;
    }

从注释看NewLifecycle<P, S, SS>是为了向下兼容设计的一个生命周期interface

 // This should be "infer SS" but can't use it yet
    interface NewLifecycle<P, S, SS> {
        /**
         * Runs before React applies the result of `render` to the document, and
         * returns an object to be given to componentDidUpdate. Useful for saving
         * things such as scroll position before `render` causes changes to it.
         *
         * Note: the presence of getSnapshotBeforeUpdate prevents any of the deprecated
         * lifecycle events from running.
         */
        getSnapshotBeforeUpdate?(prevProps: Readonly<P>, prevState: Readonly<S>): SS | null;
        /**
         * Called immediately after updating occurs. Not called for the initial render.
         *
         * The snapshot is only present if getSnapshotBeforeUpdate is present and returns non-null.
         */
        componentDidUpdate?(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot?: SS): void;
    }

自此从React实例到生命周期分析完了。上面提到的interface Component接下来会继续分析。

 type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;

组件类型的定义:ComponentType<P = {}>定义为实现了ComponentClass<P>接口的类或者实现了FunctionComponent<P>接口的类 。

下面是ComponentClass<P> interface 的定义:

interface ComponentClass<P = {}, S = ComponentState> extends StaticLifecycle<P, S>
// Unfortunately, we have no way of declaring that the component constructor must implement this
interface StaticLifecycle<P, S> {
    getDerivedStateFromProps?: GetDerivedStateFromProps<P, S>;
    getDerivedStateFromError?: GetDerivedStateFromError<P, S>;
}

FunctionComponent<P> interface 的定义:

interface FunctionComponent<P = {}> {
        (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
        propTypes?: WeakValidationMap<P>;
        contextTypes?: ValidationMap<any>;
        defaultProps?: Partial<P>;
        displayName?: string;
}

此外还要提到PureComponent,这个直接继承Component的类。可以看到区别是:前面两个都是单独定义的interface.

class PureComponent<P = {}, S = {}, SS = any> extends Component<P, S, SS> { }

PureComponentComponent 几乎完全相同,但 PureComponent 通过prop和state的浅对比来实现 shouldComponentUpate()

如果React组件的 render() 函数在给定相同的props和state下渲染为相同的结果,在某些场景下你可以使用 PureComponent 来提升性能。

PureComponentshouldComponentUpdate() 只会对对象进行浅对比(shallowly compares)。如果对象包含复杂的数据结构,它可能会因深层的数据不一致而产生错误的否定判断(表现为对象深层的数据已改变视图却没有更新, 原文:false-negatives)。当你期望只拥有简单的props和state时,才去继承 PureComponent ,或者在你知道深层的数据结构( deep data structures )已经发生改变时使用 forceUpate() 。或者,考虑使用 不可变对象(immutable objects) 来促进嵌套数据( nested data )的快速比较。

此外,PureComponentshouldComponentUpate() 会忽略整个组件的子级( component subtree)。请确保所有的子级组件也是”Pure”的。

题外话: FunctionComponent 不再被认为是’stateless’的。
原因是你可以使用const [state, setState] = useState(initialState);自由的使用state。

/**
     * @deprecated as of recent React versions, function components can no
     * longer be considered 'stateless'. Please use `FunctionComponent` instead.
     *
     * @see [React Hooks](https://reactjs.org/docs/hooks-intro.html)
     */
    type StatelessComponent<P = {}> = FunctionComponent<P>;

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.

Note:
React 16.8.0 is the first release to support Hooks. When upgrading, don’t forget to update all packages, including React DOM. React Native supports Hooks since the 0.59 release of React Native.