TypeScript+React 實(shí)踐

使用 create-react-app 開啟 TypeScript

Create React App 是一個(gè)官方支持的創(chuàng)建 React 單頁(yè)應(yīng)用程序的CLI,它提供了一個(gè)零配置的現(xiàn)代構(gòu)建設(shè)置。當(dāng)你使用 Create React App 來(lái)創(chuàng)建一個(gè)新的 TypeScript React 工程時(shí),你可以運(yùn)行:

 npx create-react-app my-app --typescript

或者

 yarn create react-app my-app --typescript

如果在已有的工程中添加,也非常簡(jiǎn)單:

 npm install --save typescript @types/node @types/react @types/react-dom @types/jest

或者

yarn add typescript @types/node @types/react @types/react-dom @types/jest 

從零配置

創(chuàng)建 index.html 文件,以及src 目錄,在 src目錄中創(chuàng)建 index.tsx

TypeScript 的文件格式是 tsx

接下來(lái)安裝必要的包和配置 package.json 文件:

"scripts": {
  "dev": "MODE=development webpack -w --mode=development",
  "build": "MODE=production webpack --mode=production"
},
"dependencies": {
  "@types/react": "^16.8.13",
  "@types/react-dom": "^16.8.3",
  "react": "^16.8.6",
  "react-dom": "^16.8.6"
},
"devDependencies": {
  "awesome-typescript-loader": "^5.2.1",
  "source-map-loader": "^0.2.4",
  "typescript": "^3.4.3",
  "webpack": "^4.29.6",
  "webpack-cli": "^3.3.0"
} 

創(chuàng)建 tsconfig.jsonwebpack.config.js 文件:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "lib": ["dom","es2015"],
    "jsx": "react",
    "sourceMap": true,
    "strict": true,
    "noImplicitAny": true,
    "baseUrl": "src",
    "paths": {
      "@/*": ["./*"],
    },
    "esModuleInterop": true,
    "experimentalDecorators": true,
  },
  "include": [
    "./src/**/*"
  ]
} 
  • jsx 選擇 react
  • lib 開啟 domes2015
  • include 選擇我們創(chuàng)建的 src 目錄
var fs = require('fs')
var path = require('path')
var webpack = require('webpack')
const { CheckerPlugin } = require('awesome-typescript-loader');
var ROOT = path.resolve(__dirname);

var entry = './src/index.tsx';
const MODE = process.env.MODE;
const plugins = [];
const config = {
  entry: entry,
  output: {
    path: ROOT + '/dist',
    filename: '[name].bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.ts[x]?$/,
        loader: [
          'awesome-typescript-loader'
        ]
      },
      {
        enforce: 'pre',
        test: /\.ts[x]$/,
        loader: 'source-map-loader'
      }
    ]
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.json'],
    alias: {
      '@': ROOT + '/src'
    }
  },
}

if (MODE === 'production') {
  config.plugins = [
    new CheckerPlugin(),
    ...plugins
  ];
}

if (MODE === 'development') {
  config.devtool = 'inline-source-map';
  config.plugins = [
    new CheckerPlugin(),
    ...plugins
  ];
}

module.exports = config; 

類組件的使用

類組件是目前來(lái)說(shuō)使用的最頻繁的一種,因此我們需要了解到它。

Props 和 State

首先創(chuàng)建 Props 和 State 接口,Props 接口接收一個(gè) name 參數(shù),State 接口接收 color:

interface IProps {
  name: string;
}

interface IState {
  color: "red" | "blueviolet"
} 

class Home extends React.Component<IProps, IState> {
  constructor(props: IProps){
    super(props);
    this.state = {
      color: "red"
    }
  }

  public onClickColor = () => {
    const { color } = this.state;
    if (color === "red") {
      this.setState({
        color: "blueviolet"
      });
    }
    if (color === "blueviolet") {
      this.setState({
        color: "red"
      });
    }
  }

  public render(){
    const { name } = this.props;
    const { color } = this.state;
    return (
      <div>
        <span style={{ color }}>{ name }</span>
        <button onClick={this.onClickColor}>變顏色</button>
      </div>
    );
  }
}

export default Home; 

App 中使用 Home 組件時(shí)我們可以得到明確的傳遞參數(shù)類型。

處理 Event 對(duì)象

有時(shí)候我們需要處理一下 Event 對(duì)象,一般 change 事件我們可以使用 React.ChangeEvent,click 事件可以使用 React.MouseEvent ,它們都接收一個(gè) Element,如:

onClickColor = (ev: React.MouseEvent<HTMLButtonElement>) => {
  //
} 

PureComponent

我們都知道 React 的刷新機(jī)制,因此如果每一次的變動(dòng)都要刷新一下界面,這對(duì)于應(yīng)用程序的性能來(lái)說(shuō)是一個(gè)非常不科學(xué)的事情,因此在沒有 PureComponent 之前,我們都需要手動(dòng)使用 shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean; 來(lái)確認(rèn)到底要不要刷新界面,如:

import * as React from "react";
import Typography from "@material-ui/core/Typography";

interface IMyComparisonProps {
  text: string;
}

class MyComparison extends React.Component<IMyComparisonProps> {
  constructor(props: IMyComparisonProps) {
    super(props);
  }

  public shouldComponentUpdate(nextProps: IMyComparisonProps) {
    if (this.props.text === nextProps.text) {
      return false;
    }
    return true;
  }

  public render() {
    const { text } = this.props;
    return (
      <Typography>
        Component 值:{ text }
      </Typography>
    );
  }
}

export default MyComparison; 

如果返回的是 false 那么將不調(diào)用 render,如果是 true 則調(diào)用 render

但是如果我們使用 PureComponent 那么就省略了這一步,我們可以不用關(guān)心組件是否要刷新,而是 React.PureComponent 來(lái)幫我們決定。在使用之前,我們還有一些注意事項(xiàng)要了解,React.PureComponent 是一個(gè)和 React.Component 幾乎相同,唯一不同的是 React.PureComponent 幫助我們完成了 shouldComponentUpdate 的一些交淺的比較,因此在我們真實(shí)的組件設(shè)計(jì)中,我們一般會(huì)用于最后一個(gè)關(guān)鍵點(diǎn)的組件上。

Portals

ReactDOM 中提供了一個(gè)方法 createPortal,可以將節(jié)點(diǎn)渲染在父組件之外,但是你可以依然使用父組件上下文中的屬性。這個(gè)特性在我所講的全局對(duì)話框或者提示框中非常有用,它脫離了父節(jié)點(diǎn)的容器,插在最外層,在樣式上就能通過(guò) position: fixed 來(lái)覆蓋整個(gè)文檔樹。

我們?cè)?state 中定義了一個(gè) open,它只接收一個(gè)布爾值,用于打開提示框或關(guān)閉提示框架,如:

export interface IPortalsProps {}

export interface IPortalsState {
  open: boolean;
} 

然后我們定義兩個(gè)方法用于設(shè)置 open

public clickHandler = () => {
  this.setState({
    open: true,
  });
}

public clickHandlerClose = () => {
  this.setState({
    open: false,
  });
} 

最后在 render 方法中使用 ReactDOM.createPortal 來(lái)創(chuàng)建一個(gè)全局的 Alert,如:

import * as React from "react";
import * as ReactDOM from "react-dom";
import Button from "@material-ui/core/Button";
import Alert from "../Alert";
import {
  IPortalsProps,
  IPortalsState,
} from "./types";

class MyPortals extends React.Component<IPortalsProps, IPortalsState> {

  constructor(props: IPortalsProps) {
    super(props);
    this.state = {
      open: false,
    };
  }

  public clickHandler = () => {
    this.setState({
      open: true,
    });
  }

  public clickHandlerClose = () => {
    this.setState({
      open: false,
    });
  }

  public render() {
    const { open } = this.state;
    return (
      <div>
        <Button
          variant="outlined"
          color="primary"
          onClick={this.clickHandler}
        >
          提示
        </Button>
        {
          ReactDOM.createPortal(
            <Alert
              open={open}
              message="React Component Portals Use"
              handleClose={this.clickHandlerClose}
            />,
            document.getElementById("app")!,
          )
        }
      </div>
    );
  }
}

export default MyPortals; 

Fragments

Fragments 可以讓我們減少生成過(guò)多有副作用的節(jié)點(diǎn),以往 render 必須返回單一節(jié)點(diǎn),因此很多組件常常會(huì)產(chǎn)生過(guò)多無(wú)用的 divReact 根據(jù)這樣的情況給予了一個(gè)組件來(lái)解決這個(gè)問(wèn)題,它就是 Fragment

public render(){
  return (
    <React.Fragment>
      <div></div>
      <div></div>
    </React.Fragment>
  )
}

//or

public render(){
  return (
    <>
      <div></div>
      <div></div>
    </>
  )
} 

函數(shù)組件以及 Hooks

Hooks 自去年10月發(fā)布以來(lái),函數(shù)組件就派上了用場(chǎng),React 的函數(shù)組件主要引用 SFC 返回(React.FunctionComponent),當(dāng)然你也可以不引用 SFC 類型只不過(guò)返回的是(JSX.Element),這就是區(qū)別。

useState

以前:

interface IFuncComp {
  name: string;
}
const FuncComp: React.SFC<IFuncComp> = ({ name }) => {
  return (
    <div>{ name }</div>
  )
} 

現(xiàn)在:

interface IFuncComp2 {
  name: string;
}

const FuncComp2: React.SFC<IFuncComp2> = ({ name }) => {
  const [ num, setNum ] = React.useState<number>(0);
  return (
    <div>
      { name } { num }
      <button onClick={() => {
        setNum(num + 1);
      }}>+</button>
    </div>
  )
} 

function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>]; 

由于 useState 被定義為一個(gè)泛型函數(shù),因此類型可以由我們自己來(lái)指定。

useEffect

當(dāng)你使用 useEffect 時(shí),我們可以傳入第三個(gè)參數(shù)來(lái)決定是否執(zhí)行這個(gè) callback ,這對(duì)于優(yōu)化你的應(yīng)用至關(guān)重要。

React.useEffect(() => {

}, [num]); 

useContext

對(duì)于 useContext 當(dāng)你需要共享數(shù)據(jù)時(shí)可用:

interface IContext {
  name: string;
}
const initContext: IContext = {
  name: "",
};
const context = React.createContext(initContext);

const FuncMainContext = () => {
  return (
    <>
      <context.Provider value={initContext}>
        <FuncContext />
      </context.Provider>
    </>
  )
}

const FuncContext = () => {
  const va = React.useContext(context);
  return (
    <div>{ va.name }</div>
  )
} 

useReducer

如果你已經(jīng)習(xí)慣 redux 不妨來(lái)看看 useReducer,假設(shè)我們需要通過(guò)按鈕來(lái)更改文本顏色:

interface IState {
  color: "red" | "blueviolet"
}

interface IAction {
  type: string;
  payload: any;
}

const reducer = (prevState: IState, action: IAction) => {
  const { type, payload } = action;
  switch(type){
    case "COLOR_CHANGE" : {
      return { ...prevState, color: payload };
    }
    default: {
      return prevState;
    }
  }
}

const App = () => {
  const initialState: IState = {
    color: "red"
  }
  const [state, dispatch ] = React.useReducer(reducer, initialState);
  return (
    <div>
      <span style={{ color: state.color }}>icepy</span>
      <button onClick={() => {
        dispatch({
          type: "COLOR_CHANGE",
          payload: state.color === "red" ? "blueviolet" : "red"
        });
      }}>change</button>
    </div>
  );
} 

useRef

當(dāng)我們需要來(lái)引用原生DOM來(lái)處理某件事情時(shí),useRef 可以輔助我們完成這項(xiàng)工作:

const App = () => {
  const inputEl = React.useRef<HTMLInputElement>(null);
  const onButtonClick = () => {
    if (inputEl && inputEl.current) {
      inputEl.current.focus();
    }
  }
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus</button>
    </>
  );
} 

useMemo

接下來(lái)我們可以說(shuō)一說(shuō) useMemo ,這只能當(dāng)作一次性能優(yōu)化的選擇,通常情況下假設(shè)我們的 state 有兩個(gè)屬性,它的場(chǎng)景可能如下:

const App = () => {
  const [ index, setIndex ] = React.useState<number>(0);
  const [ str, setStr ] = React.useState<string>("");
  const add = () => {
    return index * 100;
  }
  return (
    <>
      <div>{index}-{str}-{add()}</div>
      <div>
        <button onClick={() => {
          setIndex(index + 1);
        }}>+</button>
        <input type="text" onChange={(ev: React.ChangeEvent<HTMLInputElement>) => {
          setStr(ev.target.value);
        }}/>
      </div>
    </>
  );
} 

無(wú)論如何修改 indexstr 都會(huì)引發(fā) add() 的執(zhí)行,這對(duì)于性能來(lái)說(shuō)是很難接受的,因?yàn)?add() 只依賴于 index ,因此我們可以使用 useMemo 來(lái)優(yōu)化此項(xiàng)。

const App = () => {
  const [ index, setIndex ] = React.useState<number>(0);
  const [ str, setStr ] = React.useState<string>("");
  const add = React.useMemo(() => {
    return index * 100;
  }, [index]);
  return (
    <>
      <div>{index}-{str}-{add}</div>
      <div>
        <button onClick={() => {
          setIndex(index + 1);
        }}>+</button>
        <input type="text" onChange={(ev: React.ChangeEvent<HTMLInputElement>) => {
          setStr(ev.target.value);
        }}/>
      </div>
    </>
  );
} 

useMemo 的類型依賴于 factory 的返回值,我們可以觀察一下它的描述文件:

function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T; 

useCallback

那么 useCallback 的使用和 useMemo 比較類似,但它返回的是緩存函數(shù)。 通常情況下,我們可以使用 useCallback 來(lái)處理父組件更新但不想子組件更新的問(wèn)題,如:

interface IAppChildProps {
  callback: () => number;
}
const AppChild = ({ callback }: IAppChildProps) => {
  const [ index, setIndex ] = React.useState(() => callback());
  React.useEffect(() => {
    setIndex(callback());
  }, [callback])
  return (
    <div> { index }</div>
  );
}

const App = () => {
  const [ index, setIndex ] = React.useState<number>(0);
  const [ str, setStr ] = React.useState<string>("");
  const callback = React.useCallback(() => {
    return index * 100;
  }, [index]);
  return (
    <>
      <h1>{ str }</h1>
      <AppChild callback={callback} />
      <div>
        <button onClick={() => {
          setIndex(index + 1);
        }}>+</button>
        <input type="text" onChange={(ev: React.ChangeEvent<HTMLInputElement>) => {
          setStr(ev.target.value);
        }}/>
      </div>
    </>
  );
} 

useImperativeHandle

useImperativeHandle 可以讓你使用 ref 將自定義的函數(shù)暴露給父組件,這種場(chǎng)景一般情況可以用于在父組件中操作子組件的DOM元素,需要和 forwardRef 配合使用:

interface IFancyInput {
  name: string;
}

interface IFancyInputRef {
  focus: () => void;
}

const fancyInput = (props: IFancyInput, ref: React.Ref<IFancyInputRef>) => {
  const inputEl = React.useRef<HTMLInputElement>(null);
  React.useImperativeHandle(ref, () => ({
    focus: () => {
      if (inputEl && inputEl.current) {
        inputEl.current.focus();
      }
    }
  }));
  return (
    <input ref={inputEl} type="text" defaultValue={props.name}/>
  );
}

const FancyInput = React.forwardRef<IFancyInputRef, IFancyInput>(fancyInput);

const App = () => {
  const fancyRef = React.useRef<IFancyInputRef>(null);
  return (
    <div>
      <FancyInput ref={fancyRef} name="icepy" />
      <button onClick={() => {
        if (fancyRef && fancyRef.current) {
          fancyRef.current.focus();
        }
      }}>+</button>
    </div>
  )
} 

在組件樹之間傳遞數(shù)據(jù)的 Context

在一個(gè)典型的 React 應(yīng)用中,數(shù)據(jù)都是通過(guò) Props 屬性自上而下進(jìn)行傳遞的,但某些情況下這些屬性有多個(gè)組件需要共享,那么 Context 就提供了這樣一種共享的方式。

當(dāng)你使用 createContext 創(chuàng)建一個(gè) Context 時(shí)它會(huì)返回一個(gè) React.Context<T> 類型。

每一個(gè) Context 對(duì)象都會(huì)返回一個(gè) Provider 組件,它允許消費(fèi)組件訂閱 context 的變化,當(dāng) Provider 的value 發(fā)生變化時(shí),它內(nèi)部的所有消費(fèi)組件都將重新渲染。

interface IContext {
  name: string;
}
const initContext:IContext = {
  name: "",
};
const Context = React.createContext(initContext);

const AppChild = () => {
  const context  = React.useContext(Context);
  return (
    <div>{context.name}</div>
  )
}

const AppChild1 = () => {
  const context  = React.useContext(Context);
  return (
    <div>{context.name}</div>
  )
}
const App = () => {
  const [ name, setName ] = React.useState("");
  return (
    <div>
      <Context.Provider value={{ name }}>
        <AppChild />
        <AppChild1 />
      </Context.Provider>
    <button onClick={() => {
      setName("icepy");
    }}>+</button>
    </div>
  )
} 

我們也可以看一個(gè)類組件的例子:

interface IContext {
  name: string;
}
const initContext:IContext = {
  name: "",
};
const Context = React.createContext(initContext);

class AppChild extends React.Component {
  static contextType = Context;
  public render(){
    const { name } = this.context;
    return (
      <div> { name }</div>
    )
  }
}
const App = () => {
  const [ name, setName ] = React.useState("");
  return (
    <div>
      <Context.Provider value={{ name }}>
        <AppChild />
      </Context.Provider>
      <button onClick={() => {
        setName("icepy");
      }}>+</button>
    </div>
  )
} 

在 TypeScript 中 Context 支持的并不算太好,如:

static contextType?: Context<any>;
/**
 * If using the new style context, re-declare this in your class to be the
 * `React.ContextType` of your `static contextType`.
 *
 * ```ts
 * static contextType = MyContext
 * context!: React.ContextType<typeof MyContext>
 * ```
 *
 * @deprecated if used without a type annotation, or without static contextType
 * @see https://reactjs.org/docs/legacy-context.html
 */
// TODO (TypeScript 3.0): unknown
context: any; 

Ref 和 DOM

Refs 提供了一種方式,允許我們?cè)L問(wèn) DOM 節(jié)點(diǎn)或在 render 方法中創(chuàng)建的 React 元素。

const App = () => {
  const but = React.createRef<HTMLButtonElement>();
  return (
    <div>
      <button ref={but} onClick={() => {
        if (but && but.current) {
          if (but.current.nodeName === "BUTTON") {
            alert("BUTTON");
          }
        }
      }}> + </button>
    </div>
  )
} 

獲取 React 對(duì)象:

class AppChild extends React.Component {

  public onButtonClick = (target: EventTarget) => {
    console.dir(target);
  }

  public render(){
    return (
      <div>1234</div>
    )
  }
}

const App = () => {
  const appChild = React.createRef<AppChild>();
  return (
    <>
      <AppChild ref={appChild}/>
      <button onClick={(ev: React.MouseEvent<HTMLButtonElement>) => {
        if (appChild && appChild.current) {
          appChild.current.onButtonClick(ev.target);
        }
      }}>+</button>
    </>
  )
} 

ref 也可以傳遞函數(shù):

const App = () => {
  const inputCallback = (el: HTMLInputElement) => {
    console.log(el);
  }
  return (
    <div>
      <input ref={inputCallback}/>
    </div>
  )
} 

對(duì)應(yīng)的 useRef() 也非常類似,它可以很方便的保存任何可變值,這是因?yàn)樗鼊?chuàng)建的是一個(gè)普通 JavaScript 對(duì)象。

const App = () => {
  const inputEl = React.useRef<HTMLInputElement>(null);
  return (
    <div>
      <input ref={inputEl} type="text"/>
      <button onClick={() => {
        if (inputEl && inputEl.current) {
          inputEl.current.focus();
        }
      }}>+</button>
    </div>
  )
} 

React 頂層其他 APIs

React 是整個(gè) React 庫(kù)的入口,頂層 APIs 中除了我們比較熟悉的如 Component 之外還有一些比較有用的,這里會(huì)介紹幾種我們不常用但非常重要的頂層 APIs。

isValidElement

驗(yàn)證對(duì)象是否為 React 對(duì)象,返回值是 truefalse

React.isValidElement(object);

cloneElement

有時(shí)我們會(huì)遇到這樣一個(gè)場(chǎng)景,就是 tabs 選項(xiàng)卡,對(duì)于它的設(shè)計(jì)我們可能會(huì)有一個(gè)預(yù)期,做一個(gè)簡(jiǎn)單版,比如:

<Tabs value={index} onChange={(value) => {
  setIndex(value);
}}>
  <Tab value={1}>Tab 1</Tab>
  <Tab value={2}>Tab 2</Tab>
  <Tab value={3}>Tab 3</Tab>
</Tabs>
<div style={{ display: index === 1 ? "block": "none"}}>1</div>
<div style={{ display: index === 2 ? "block": "none"}}>2</div>
<div style={{ display: index === 3 ? "block": "none"}}>3</div> 

點(diǎn)擊 Tab 的時(shí)候需要把它的 onClick 事件替換成 Tabs 的 onChange,因此這里會(huì)使用到 cloneElement 方法來(lái)處理。

interface ITabsProps {
  value: number;
  onChange: (value: number) => void;
  children?: React.ReactNode;
}

const tabsStyles: React.CSSProperties = {
  width: "100%",
  display: "flex",
  flexDirection: "row",
}

const Tabs = (props: ITabsProps) => {
  const onChange = (value: number) => {
    props.onChange(value);
  }
  const renderTab = () => {
    const { children } = props;
    if (children && Array.isArray(children)) {
      const arrayChilds = children.map((v, i) => {
        if (React.isValidElement(v)) {
          const childrenProps = {
            onChange,
            key: `Tab-${i}`,
          };
          return React.cloneElement(v, childrenProps);
        }
      });
      return arrayChilds;
    }
    if (children && !Array.isArray(children)) {
      const childrenProps = {
        onChange,
        key: "Tab",
      };
      if (React.isValidElement(children)) {
        return React.cloneElement(children, childrenProps);
      }
    }
  }

  return (
    <div style={tabsStyles}>
      {renderTab()}
    </div>
  );
} 

由于我們把 childrenProps 替換了,因此子元素的 Tab 就可以如此:

interface ITabProps {
  value: number;
  onChange?: (value: number) => void;
  children?: React.ReactNode;
}

const tabStyles: React.CSSProperties = {
  width: "50px",
  marginRight: "10px",
  border: "1px solid red",
  textAlign: "center",
  cursor: "pointer"
}

const Tab = (props: ITabProps) => {
  const changeHandler = () => {
    const { onChange, value } = props;
    if (onChange) {
      onChange(value);
    }
  }
  return (
    <div
      style={tabStyles}
      onClick={changeHandler}
    >
      { props.children }
    </div>
  );
} 

memo

React.memo 為高階組件。它與 React.PureComponent 非常相似,但它適用于函數(shù)組件,但不適用于 class 組件。

此方法僅作為性能優(yōu)化的方式而存在。

interface IProps {
  value: number;
}

const AppChild = (props: IProps) => {
  return (
    <div>props.value: { props.value}</div>
  )
}

const MemoAppChild = React.memo(AppChild);

interface IState {
  date: Date;
  value: number;
}

class App extends React.Component<{}, IState> {

  constructor(props: {}){
    super(props);
    this.state = {
      value: 0,
      date: new Date(),
    }
  }

  public componentDidMount(){
    setInterval(()=>{
      this.setState({
        date:new Date()
      })
    },1000)
  }

  public render(){
    return (
      <div>
        <MemoAppChild value={this.state.value} />
        <div>
          { this.state.date.toString() }
        </div>
      </div>
    );
  }
} 

如果你想更細(xì)節(jié)的控制,可以傳入第二個(gè)參數(shù),它是一個(gè)函數(shù):

interface IProps {
  value: number;
}

const AppChild = (props: IProps) => {
  return (
    <div>props.value: { props.value}</div>
  )
}

type Equal = (prevProps: IProps, nextProps: IProps) => boolean;

const areEqual: Equal = (prevProps, nextProps) => {
  if (prevProps.value === nextProps.value) {
    return true;
  } else {
    return false;
  }
}
const MemoAppChild = React.memo(AppChild, areEqual);

interface IState {
  date: Date;
  value: number;
}

class App extends React.Component<{}, IState> {

  constructor(props: {}){
    super(props);
    this.state = {
      value: 0,
      date: new Date(),
    }
  }

  public componentDidMount(){
    setInterval(()=>{
      this.setState({
        date:new Date()
      })
    },1000)
  }

  public render(){
    return (
      <div>
        <MemoAppChild value={this.state.value} />
        <div>
          { this.state.date.toString() }
        </div>
      </div>
    );
  }
} 
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容