## 入门

### 介绍

React 是一个用于构建用户界面的 JavaScript 库。

- React 官方文档 (reactjs.org)
- Styled Components 备忘清单 (jaywcjlove.github.io)

    import {createRoot} from 'react-dom/client'
    import App from './App'

    const elm = document.getElementById('app')
    const root = createRoot(elm);
    root.render(<App />);

#### 快速创建 React 项目 (CRA)

    npx create-react-app my-app

### 导入多个导出

    import React, {Component} from 'react'
    import ReactDOM from 'react-dom'

    export class Hello extends Component {
      ...
    }
    export default function World() {
      /* ... */
    }

使用 `export` 导出 `Hello`，`export default` 导出 `World` 组件

    import World, { Hello } from './hello.js';

使用 `import` 导入 `Hello` 组件，在示例中使用。

### React 组件中的 CSS

    import React from "react";
    import "./Student.css";

    export const Student = (
      <div className="Student"></div>
    );

注意：类属性 `className`

    const divStyle = {
      backgroundImage: 'url(' + imgUrl + ')',
    };
    export const Student = (
      <div style={divStyle}></div>
    );

### 属性

    <Student name="Julie" age={23}
      pro={true} />

函数组件 `Student` 中访问属性

    function Student(props) {
      return <h1>Hello, {props.name}</h1>;
    }

Class 组件 `Student` 中访问属性

    class Student extends React.Component {
      render() {
        return (
          <h1>Hello, {this.props.name}</h1>
        );
      }
    }

`class` 组件使用 `this.props` 访问传递给组件的属性。

### Children

    function Example() {
      return (
        <AlertBox>
          <h1>您有待处理的通知</h1>
        </AlertBox>
      )
    }

函数 `AlertBox` 组件

    function AlertBox(props) {
      return (
        <div className="alert-box">
          {props.children}
        </div>
      );
    }

    {props.children}

Class `AlertBox` 组件，与函数组件 `AlertBox` 组件相同

    class AlertBox extends React.Component {
      render () {
        return (
          <div className="alert-box">
            {this.props.children}
          </div>
        );
      }
    }

    {this.props.children}

`children` 作为子组件的的属性传递。

### State

函数中的 State，Hook 是 React 16.8 的新增特性

    import { useState } from 'react';

    function Student() {
      const [count, setCount] = useState(0);
      const click = () => setCount(count + 1);
      return (
        <div>
          <p>您点击了 {count} 次</p>
          <button onClick={click}>
            点击我
          </button>
        </div>
      );
    }

使用 `setState` 更新状态，下面是函数组件读取状态

    <p>您点击了 {count} 次</p>

#### Class 中的 State

    import React from 'react';

    class Student extends React.Component {
      constructor(props) {
        super(props);
        this.state = {count: 1};
        // 确保函数可以访问组件属性（ES2015）
        this.click = this.click.bind(this);
      }
      click() {
        const count = this.state.count;
        this.setState({ count: count + 1})
      }
      render() {
        return (
          <div>
            <button onClick={this.click}>
              点击我
            </button>
            <p>您点击了{this.state.count}次</p>
          </div>
        );
      }
    }

使用 `setState` 更新状态，`class` 组件中不能使用 <yel>hooks</yel>。下面是 `class` 组件读取状态

    <p>您点击了{this.state.count}次</p>

### 循环

    const elm = ['one', 'two', 'three'];
    function Student() {
      return (
        <ul>
          {elm.map((value, index) => (
            <li key={index}>{value}</li>
          ))}
        </ul>
      );
    }

`key` 值在兄弟节点之间必须唯一

### 事件监听

    export default function Hello() {
      function handleClick(event) {
        event.preventDefault();
        alert("Hello World");
      }

      return (
        <a href="/" onClick={handleClick}>
          Say Hi
        </a>
      );
    }

### 函数注入

    function addNumbers(x1, x2) {
      return x1 + x2;
    }

    const element = (
      <div>
        {addNumbers(2, 5)}
      </div>
    );

### 嵌套

    import { useState } from 'react'
    import Avatar from './Avatar';
    import Profile from './Profile';

    function Student() {
      const [count, setCount] = useState(0);
      return (
        <div>
          <Avatar src={count} />
          <Profile username={count} />
        </div>
      );
    }

### Portals

React 并_没有_创建一个新的 `div`。它只是把子元素渲染到 `domNode` 中。`domNode` 是一个可以在任何位置的有效 DOM 节点。

    render() {
      return ReactDOM.createPortal(
        this.props.children,
        domNode
      );
    }

提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案

### Fragment

    import { Fragment } from 'react'
    import Avatar from './Avatar';
    import Profile from './Profile';

    const Student = () => (
      <Fragment>
        <Avatar src="./demo.jpg" />
        <Profile username="name" />
      </Fragment>
    );

从 `v16.2.0` 开始 `Fragment` 可用于返回多个子节点，而无需向 DOM 添加额外的包装节点。或者使用 `<></>` 效果是一样的。

    const Student = () => (
      <>
        <Avatar src="./demo.jpg" />
        <Profile username="name" />
      </>
    );

查看: Fragments & strings

### 返回字符串

    render() {
      return 'Look ma, no spans!';
    }

您可以只返回一个字符串。查看: Fragments & strings

### 返回数组

    const Student = () => [
      <li key="A">First item</li>,
      <li key="B">Second item</li>
    ];

不要忘记 `key`！查看: Fragments & strings

### Refs 转发

    const FancyButton = React.forwardRef(
      (props, ref) => (
        <button ref={ref} className="btn">
          {props.children}
        </button>
      )
    );

#### 使用

    // 你可以直接获取 DOM button 的 ref：
    const ref = React.createRef();

    <FancyButton ref={ref}>
      点击我
    </FancyButton>;

### Class 组件内部使用 ref 属性

    import {Component,createRef} from 'react'

    class MyComponent extends Component {
      constructor(props) {
        super(props);
        this.myRef = createRef();
      }

      render() {
        return <div ref={this.myRef} />;
      }
    }

提示：Refs 适用于类组件，但不适用于函数组件（除非您使用 useRef hook，请参阅hooks）

### 函数组件内部使用 ref 属性

    function CustomTextInput(props) {
      // 这里必须声明 $input，这样 ref 才可以引用它
      const $input = useRef(null);
      function handleClick() {
        $input.current.focus();
      }
      return (
        <div>
          <input type="text" ref={$input} />
          <input
            type="button" value="聚焦文本输入"
            onClick={handleClick}
          />
        </div>
      );
    }

### 严格模式 StrictMode

    <div>
      <Header />
      <React.StrictMode>
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>
      <Footer />
    </div>

- 识别不安全的生命周期
- 关于使用过时字符串 ref API 的警告
- 关于使用废弃的 findDOMNode 方法的警告
- 检测意外的副作用
- 检测过时的 context API
- 确保可复用的状态

突出显示应用程序中潜在问题的工具。请参阅：严格模式

### Profiler

测量一个 React 应用多久渲染一次以及渲染一次的 `代价`

    <Profiler id="Navigation" onRender={callback}>
      <Navigation {...props} />
    </Profiler>

为了分析 `Navigation` 组件和它的子代。应该在需要时才去使用它。

:- | :-
--- | ---
`id(string)` | 发生提交的 `Profiler` 树的 `id`
`onRender(function)` | 组件树任何组件 “提交” 一个更新的时候调用这个函数

#### onRender 回调函数

:- | :-
--- | ---
`phase: "mount" | "update"` | 判断是由 `props`/`state`/`hooks` 改变 或 “第一次装载” 引起的重渲染
`actualDuration: number` | 本次更新在渲染 Profiler 和它的子代上花费的时间
`baseDuration: number` | 在 Profiler 树中最近一次每一个组件 render 的持续时间
`startTime: number` | 本次更新中 React 开始渲染的时间戳
`commitTime: number` | 本次更新中 React commit 阶段结束的时间戳
`interactions: Set` | 当更新被制定时，“interactions” 的集合会被追踪

## 默认值

### Class 组件默认 props

    class CustomButton extends React.Component {
      // ...
    }
    CustomButton.defaultProps = {
      color: 'blue'
    };

#### 使用

    <CustomButton /> ;

不传值 `props.color` 将自动设置为 `blue`

### Class 组件默认 state

    class Hello extends Component {
      constructor (props) {
        super(props)
        this.state = { visible: true }
      }
    }

在构造 `constructor()`中设置默认状态。

    class Hello extends Component {
      state = { visible: true }
    }

### 函数组件默认 props

    function CustomButton(props) {
      const { color = 'blue' } = props;
      return <div>{color}</div>
    }

### 函数组件默认 state

    function CustomButton() {
      const [color, setColor]=useState('blue')
      return <div>{color}</div>
    }

## JSX

### 介绍

`JSX` 仅仅只是 `React.createElement(component, props, ...children)` 函数的语法糖

    <MyButton color="blue" shadowSize={2}>
      点击我
    </MyButton>

会编译为

    React.createElement(
      MyButton,
      {color: 'blue', shadowSize: 2},
      '点击我'
    );

没有子节点

    <div className="sidebar" />

会编译为

    React.createElement(
      'div',
      {className: 'sidebar'}
    )

### JSX 点语法

    const Menu = ({ children }) => (
      <div className="menu">{children}<div>
    );

    Menu.Item = ({ children }) => (
      <div>{children}<div>
    );

    <Menu>
      <Menu.Item>菜单一</Menu.Item>
      <Menu.Item>菜单二</Menu.Item>
    <Menu>

### JSX Element

    let element = <h1>Hello, world!</h1>;
    let emptyHeading = <h1 />;

    const root = ReactDOM.createRoot(
      document.getElementById('root')
    );

    const element = <h1>Hello, world</h1>;
    root.render(element);

参考：渲染元素

### JSX 属性

    const avatarUrl = "img/picture.jpg"
    const element = <img src={avatarUrl} />;

    const element = (
      <button className="btn">
        点击我
      </button>
    );

注意：类属性 `className`

### JSX 表达式

    let name = '张三';
    let element = <h1>Hello, {name}</h1>;

    function fullName(firstName, lastName) {
      return firstName + ' ' + lastName;
    }
    let element = (
      <h1>
        Hello, {fullName('三', '张')}
      </h1>
    );

### JSX style

    const divStyle = {
      color: 'blue',
      backgroundImage: 'url(' + imgUrl + ')',
    };
    function MyComponent() {
      return <div style={divStyle}>组件</div>;
    }

### JSX dangerouslySetInnerHTML

    const markup = {__html: '我 &middot; 你' };

    const MyComponent = () => (
      <div dangerouslySetInnerHTML={markup} />
    );

`dangerouslySetInnerHTML` 是 React 为浏览器 DOM 提供 `innerHTML` 的替换方案。

### JSX htmlFor

    const MyComponent = () => (
      <div>
        <input type="radio" id="ab" name="v">
        <label for="ab">HTML</label>
      </div>
    );

`for` 在 `JS` 中是保留字，JSX 元素使用了 `htmlFor` 代替

### JSX defaultValue

非受控组件的属性，设置组件第一次挂载时的 `value`

    <textarea defaultValue="Hello" />

`<input>`、`<select>` 和 `<textarea>` 支持 value 属性

### JSX defaultChecked

非受控组件的属性，设置组件是否被选中

    <input type="radio" defaultChecked />

类型为 `checkbox` 或 `radio` 时，组件支持 checked 属性

### JSX className

属性用于指定 `CSS` 的 `class`

    <div className="warp">...</div>

React 中使用 Web Components 使用 `class` 属性代替

### JSX 条件渲染

    import React from "react";

    function formatName(user) {
      return user.firstName
        + ' '
        + user.lastName;
    }

    export function Greeting(user) {
      if (user) {
        return (
          <h1>你好, {formatName(user)}!</h1>
        );
      }
      return (
        <h1>你好, 先生。</h1>
      );
    }

注意：组件必须总是返回一些东西。

#### 使用

    <Greeting firstName="三" lastName="张" />

### JSX 三目运算符 / 与运算符 &&

    export default function Weather(props) {
      const isLoggedIn = props.isLoggedIn;
      return (
        <div>
          <b>{isLoggedIn ? '已' : '未'}</b>登录。
        </div>
      );
    }

    {isShow && <div>内容</div>}

### JSX 组件

    <Dropdown>
      下拉列表
      <Menu>
        <Menu.Item>菜单一</Menu.Item>
        <Menu.Item>菜单二</Menu.Item>
        <Menu.Item>菜单三</Menu.Item>
      </Menu>
    </Dropdown>

组件名称以大驼峰式命名。

### JSX 元素变量

    function Greeting(props) {
      let button;
      if (props.isLoggedIn) {
        button = <UserGreeting />;
      } else {
        button = <GuestGreeting />;
      }
      return <div>{button}</div>;
    }

### JSX 注释

    function Student() {
      const [count, setCount] = useState(0);
      return (
        <Fragment>
          {/* 这里写注释 */}
        </Fragment>
      );
    }

## 组件

### 函数组件

    import React from 'react';

    const UserName = () => <h1>Kenny</h1>;

    export default function UserProfile() {
      return (
        <div className="UserProfile">
          <div>Hello</div>
          <UserName />
        </div>
      );
    }

注意：每个组件都需要一个根元素，更多说明。

### Class 组件

    class Welcome extends React.Component {
      render() {
        return <h1>{this.props.name}</h1>;
      }
    }

### Class 组件 API

#### 额外的 API

:- | -
--- | ---
`this.forceUpdate()` | 强制重新渲染
`this.setState({ ... })` | 更新状态
`this.setState(state =>{ ... })` | 更新状态

#### 属性

:- | -
--- | ---
`defaultProps` | 默认 props
`displayName` | 显示组件名称(用于调试)

#### 实例属性

:- | -
--- | ---
`this.props` | 组件接受参数
`this.state` | 组件内状态

### Pure 组件

    import React, {PureComponent} from 'react'

    class MessageBox extends PureComponent {
      ···
    }

### 高阶组件

    import React, { Component } from 'react';
    // 高阶组件 with
    const with = data => WrappedComponent => {
      return class extends Component {
        constructor(props) {
          super(props);
        }
        render() {
          return (
            <WrappedComponent data={data} />
          )
        }
      }
    }

使用高阶组件

    const LowComponent = (props) => (
      <div>{props.data}</div>
    );

    const MyComp = with('Hello')(LowComponent)

### 包含关系

    function FancyBorder(props) {
      return (
        <div className={'Fancy'+props.color}>
          {props.children}
        </div>
      );
    }

组件可以通过 JSX 嵌套

    function WelcomeDialog() {
      return (
        <FancyBorder color="blue">
          <h1 className="title">欢迎</h1>
          <p className="message">
            感谢您访问我们的宇宙飞船
          </p>
        </FancyBorder>
      );
    }

### 作为参数传递

    function SplitPane(props) {
      return (
        <div className="SplitPane">
          <div className="left">
            {props.left}
          </div>
          <div className="right">
            {props.right}
          </div>
        </div>
      );
    }

    function App() {
      return (
        <SplitPane
          left={<Contacts />}
          right={<Chat />}
        />
      );
    }

给组件 `SplitPane` 传递 `left` 和 `right` 两个组件参数

### 嵌入内部组件

    import React from 'react';
    import UserAvatar from "./UserAvatar";

    export default function UserProfile() {
      return (
        <div className="UserProfile">
          <UserAvatar />
          <UserAvatar />
        </div>
      );
    }

注意：假设 `UserAvatar` 在 `UserAvatar.js` 中声明

### 嵌入外部组件

    import React from 'react';
    import {Button} from 'uiw';
    export default function UserProfile() {
      return (
        <div className="UserProfile">
          <Button type="primary">
            主要按钮
          </Button>
        </div>
      );
    }

注意：uiw 组件在 npmjs.com 上找到，需要先安装导入

### 点组件语法技巧

    const Menu = ({ children }) => (
      <div className="menu">{children}<div>
    );

    Menu.Item = ({ children }) => (
      <div>{children}<div>
    );

    <Menu>
      <Menu.Item>菜单一</Menu.Item>
      <Menu.Item>菜单二</Menu.Item>
    <Menu>

## Hooks

### Hooks API 参考

#### 基础 Hook

方法 | 描述
--- | ---
`useState` | 返回一个 `state`，更新 `state` 的函数 #
`useEffect` | 可能有副作用代码的函数 #
`useContext` | 接收并返回该 `context` 的当前值 #

#### 额外的 Hook

方法 | 描述
--- | ---
`useReducer` | `useState` 的替代方案 #
`useCallback` | 返回一个回调函数 #
`useMemo` | 返回一个 memoized 值#
`useRef` | 返回一个可变的 `ref` 对象 #
`useImperativeHandle` | 暴露给父组件的实例值 #
`useLayoutEffect` | DOM 变更后同步调用函数 #
`useDebugValue` | 开发者工具中显示标签 #
`useDeferredValue` | 接受并返回该值的新副本 #
`useTransition` | 过渡任务的等待状态 #
`useId` | 用于生成唯一 ID #

#### Library Hooks

方法 | 描述
--- | ---
`useSyncExternalStore` | 读取和订阅外部数据源 #
`useInsertionEffect` | DOM 突变之前 同步触发 #

### 函数式更新

    function Counter({ initialCount }) {
      const [count, setCount] = useState(initialCount);
      return (
        <>
          Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button>
          <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
          <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
        </>
      );
    }

### useRef

    function TextInputWithFocusButton() {
      const $input = useRef(null);
      const onButtonClick = () => {
        $input.current.focus();
      };
      return (
        <>
          <input ref={$input} type="text" />
          <button onClick={onButtonClick}>
            聚焦输入
          </button>
        </>
      );
    }

`current` 指向已挂载到 DOM 上的文本输入元素

### useImperativeHandle

    function FancyInput(props, ref) {
      const inputRef = useRef();
      useImperativeHandle(ref, () => ({
        focus: () => {
          inputRef.current.focus();
        }
      }));
      return <input ref={inputRef} />;
    }
    FancyInput = forwardRef(FancyInput);

父组件使用

    <FancyInput ref={inputRef} />
    inputRef.current.focus()

### useEffect

    useEffect(() => {
      const subs = props.source.subscribe();
      return () => {
        subs.unsubscribe();
      };
    }, [props.source]);

### useCallback

    const memoizedCallback = useCallback(
      () => {
        doSomething(a, b);
      },
      [a, b],
    );

### useMemo

    const memoizedValue = useMemo(
      () => {
        return computeExpensiveValue(a, b)
      },
      [a, b]
    );

### useId

    function Checkbox() {
      const id = useId();
      return (
        <>
          <label htmlFor={id}>
            你喜欢React吗？
          </label>
          <input id={id} type="checkbox" />
        </>
      );
    };

用于生成跨服务端和客户端稳定的唯一 `ID` 的同时避免 `hydration` 不匹配

### useDebugValue

    function useFriendStatus(friendID) {
      const [
        isOnline, setIsOnline
      ] = useState(null);
      // ...
      // 在开发者工具中的这个 Hook 旁边显示标签
      // e.g. "FriendStatus: Online"
      useDebugValue(
        isOnline ? 'Online' : 'Offline'
      );
      return isOnline;
    }

不推荐你向每个自定义 `Hook` 添加 `debug` 值

### componentDidMount & componentWillUnmount

    useEffect(
      () => {
        // componentDidMount
        // 组件挂载时，可以在这里完成你的任务
        return () => {
          // componentWillUnmount
          // 卸载时执行，清除 effect
        };
      },
      [ ]
    );

这是一个类似 `class` 组件中 `componentDidMount` & `componentWillUnmount` 两个生命周期函数的写法。

## 生命周期

### 挂载

方法 | 描述
--- | ---
`constructor` (props) | 渲染前 #
`static getDerivedStateFromProps()` | 调用 `render` 方法之前调用 #
`render()` | `class` 组件中唯一必须实现的方法 #
`componentDidMount()` | 在组件挂载后(插入 DOM 树中)立即调用 #
`UNSAFE_componentWillMount()` | 在挂载之前被调用，建议使用 `constructor()` #

在 `constructor()` 上设置初始状态。在 `componentDidMount()` 上添加 DOM 事件处理程序、计时器（等），然后在 `componentWillUnmount()` 上删除它们。

### 卸载

方法 | 描述
--- | ---
`componentWillUnmount()` | 在组件卸载及销毁之前直接调用 #

### 过时 API

过时方法 | 新方法
--- | ---
`componentWillMount()` | `UNSAFE_componentWillMount()` #
`componentWillReceiveProps()` | `UNSAFE_componentWillReceiveProps()` #
`componentWillUpdate()` | `UNSAFE_componentWillUpdate()` #

17+ 之后不再支持，在 `17` 版本之后，只有新的 `UNSAFE_` 生命周期名称可以使用。

### 更新

方法 | 描述
--- | ---
`static getDerivedStateFromProps(props, state)` | 调用 `render` 之前调用，在初始挂载及后续更新时都会被调用 #
`shouldComponentUpdate(nextProps, nextState)` | 如果返回 `false`，则跳过 `render()` #
`render()` | 在不修改组件 `state` 的情况下，每次调用时都返回相同的结果 #
`getSnapshotBeforeUpdate()` | 在发生更改之前从 DOM 中捕获一些信息（例如，滚动位置） #
`componentDidUpdate()` | 这里使用 `setState()`，但记得比较 `props`。首次渲染不会执行此方法 #

### 错误处理

方法 | 描述
--- | ---
`static getDerivedStateFromError(error)` | 后代组件抛出错误后被调用，它将抛出的错误作为参数，并返回一个值以更新 `state` #
`componentDidCatch(error, info)` | 在后代组件抛出错误后被调用，会在“提交”阶段被调用，因此允许执行副作用 #

### render()

    class Welcome extends React.Component {
      render() {
        return <h1>Hello, {this.props.name}</h1>;
      }
    }

### constructor()

    constructor(props) {
      super(props);
      // 不要在这里调用 this.setState()
      this.state = { counter: 0 };
      this.handleClick = this.handleClick.bind(this);
    }

### static getDerivedStateFromError()

    class ErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }

      static getDerivedStateFromError(error) {
        // 更新 state 使下一次渲染可以显降级 UI
        return { hasError: true };
      }

      render() {
        if (this.state.hasError) {
          // 你可以渲染任何自定义的降级  UI
          return <h1>Something went wrong.</h1>;
        }

        return this.props.children;
      }
    }

### componentDidUpdate()

    componentDidUpdate(prevProps) {
      // 典型用法（不要忘记比较 props）：
      if (this.props.uid !== prevProps.uid) {
        this.fetchData(this.props.uid);
      }
    }

### getSnapshotBeforeUpdate()

    getSnapshotBeforeUpdate(prevProps, prevState) {
      // 我们是否在 list 中添加新的 items ？
      // 捕获滚动​​位置以便我们稍后调整滚动位置。
      if (prevProps.list.length < this.props.list.length) {
        const list = this.listRef.current;
        return list.scrollHeight - list.scrollTop;
      }
      return null;
    }

## PropTypes 属性类型检查

### PropTypes

    import PropTypes from 'prop-types'

:- | -
--- | ---
`any` | 任意类型
`(props, propName, 组件名称)=>{}` | 自定义验证器

#### 基础

:- | -
--- | ---
`string` | 字符串
`number` | 数组
`func` | 函数
`bool` | 布尔值
`symbol` | -

#### 枚举 Enum

:- | -
--- | ---
`oneOf(any)` | 枚举类型
`oneOfType([type])` | 几种类型中的任意一个类型

#### 数组 Array

:- | -
--- | ---
`array` | 数组
`arrayOf` | 数组由某一类型的元素组成

#### 对象 Object

:- | -
--- | ---
`object` | 对象
`objectOf` | 对象由某一类型的值组成
`instanceOf(...)` | 类的实例
`shape` | 对象由特定的类型值组成
`exact` | 有额外属性警告

#### 元素 Elements

:- | -
--- | ---
`element` | React 元素
`elementType` | React 元素类型(即 `MyComponent`)
`node` | DOM 节点

#### 必需的

:- | -
--- | ---
`(···).isRequired` | 必需的

请参阅：使用 PropTypes 进行类型检查

### 基本类型

    MyComponent.propTypes = {
      email:      PropTypes.string,
      seats:      PropTypes.number,
      callback:   PropTypes.func,
      isClosed:   PropTypes.bool,
      any:        PropTypes.any
      symbol:     PropTypes.symbol,
    }

你可以将属性声明为 JS 原生类型，默认都是可选的。

### 必需的

    MyComponent.propTypes = {
      // 确保这个 prop 没有被提供时，会打印警告信息
      requiredFunc: PropTypes.func.isRequired,

      // 任意类型的必需数据
      requiredAny: PropTypes.any.isRequired,
    }

你可以在任何 `PropTypes` 属性后面加上 `isRequired`。

### 枚举

    MyComponent.propTypes = {
      // 只能是特定的值，枚举类型。
      optionalEnum: PropTypes.oneOf([
        'News', 'Photos'
      ]),
      // 一个对象可以是几种类型中的任意一个类型
      optionalUnion: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.number,
        PropTypes.instanceOf(Message)
      ]),
    }

### 元素 Elements

    MyComponent.propTypes = {
      // 任何可被渲染的元素
      // (包括数字、字符串、元素或数组)
      // (或 Fragment) 也包含这些类型。
      node: PropTypes.node,

      // 一个 React 元素。
      element: PropTypes.element,

      // 一个 React 元素类型（即，MyComponent）
      elementType: PropTypes.elementType,
    }

### 对象 Object

    MyComponent.propTypes = {
      // 可以指定一个对象由某一类型的值组成
      objectOf: PropTypes.objectOf(
        PropTypes.number
      ),
      // 可以指定一个对象由特定的类型值组成
      objectWithShape: PropTypes.shape({
        color: PropTypes.string,
        fontSize: PropTypes.number
      }),
      // 带有额外属性警告的对象
      objectWithStrictShape: PropTypes.exact({
        name: PropTypes.string,
        quantity: PropTypes.number
      }),
    }

### 自定义验证器

    MyComponent.propTypes = {
      custom: (props, propName, compName) => {
        if (!/matchm/.test(props[propName])) {
          // 它在验证失败时应返回一个 Error 对象
          return new Error(
            '无效的prop `'
            ` \`${propName}\` 提供给` +
            ` \`${compName}\`。验证失败。`
          );

        }
      },
    }

请不要使用 `console.warn` 或抛出异常，因为这在 `oneOfType` 中不会起作用。

### 自定义的 `arrayOf` 或 `objectOf` 验证器

    MyComponent.propTypes = {
      arrayProp: PropTypes.arrayOf((propValue, key, componentName, location, propFullName) => {
        if (!/matchme/.test(propValue[key])) {
          // 它应该在验证失败时返回一个 Error 对象。
          return new Error(
            'Invalid prop `' + propFullName + '` supplied to' +
            ' `' + componentName + '`. Validation failed.'
          );
        }
      })
    }

`propValue` 是数组或对象本身，`key` 是他们当前的键。

### 数组

    MyComponent.propTypes = {
      arr: PropTypes.arrayOf(PropTypes.number),
    };

可以指定一个数组由某一类型的元素组成

### 验证类的实例

    MyComponent.propTypes = {
      message: PropTypes.instanceOf(Message),
    };

声明 `message` 为类的实例

## 另见

- React 官方中文文档 (zh-hans.reactjs.org)
- 反应生命周期方法图 (projects.wojtekmaj.pl)
- React 16 Cheat Sheet
- Awesome React (github.com)