• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

React之hook/class结合typescript笔记

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

使用 create-react-app 开启 TypeScript

Create React App 是一个官方支持的创建 React 单页应用程序的CLI,它提供了一个零配置的现代构建设置。当你使用 Create React App 来创建一个新的 TypeScript React 工程时,你可以运行

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

 

 yarn create react-app my-app --typescript

 

如果在已有的工程中添加,也非常简单:

 npm install --save typescript

或者

yarn add typescript

从零配置

创建 index.html 文件,以及src 目录,在 src目录中创建 index.tsx

TypeScript 的文件格式是 tsx

接下来安装必要的包和配置 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"
} 

创建 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 选择我们创建的 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; 

类组件的使用

类组件是目前来说使用的最频繁的一种,因此我们需要了解到它。

Props 和 State

首先创建 Props 和 State 接口,Props 接口接收一个 name 参数,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 组件时我们可以得到明确的传递参数类型。

处理 Event 对象

有时候我们需要处理一下 Event 对象,一般 change 事件我们可以使用 React.ChangeEvent,click 事件可以使用 React.MouseEvent ,它们都接收一个 Element,如:

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

PureComponent

我们都知道 React 的刷新机制,因此如果每一次的变动都要刷新一下界面,这对于应用程序的性能来说是一个非常不科学的事情,因此在没有 PureComponent 之前,我们都需要手动使用 shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean; 来确认到底要不要刷新界面,如:

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 那么将不调用 render,如果是 true 则调用 render

但是如果我们使用 PureComponent 那么就省略了这一步,我们可以不用关心组件是否要刷新,而是 React.PureComponent 来帮我们决定。在使用之前,我们还有一些注意事项要了解,React.PureComponent 是一个和 React.Component 几乎相同,唯一不同的是 React.PureComponent 帮助我们完成了 shouldComponentUpdate 的一些交浅的比较,因此在我们真实的组件设计中,我们一般会用于最后一个关键点的组件上。

Portals

ReactDOM 中提供了一个方法 createPortal,可以将节点渲染在父组件之外,但是你可以依然使用父组件上下文中的属性。这个特性在我所讲的全局对话框或者提示框中非常有用,它脱离了父节点的容器,插在最外层,在样式上就能通过 position: fixed 来覆盖整个文档树。

我们在 state 中定义了一个 open,它只接收一个布尔值,用于打开提示框或关闭提示框架,如:

export interface IPortalsProps {}

export interface IPortalsState {
  open: boolean;
} 

然后我们定义两个方法用于设置 open

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

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

最后在 render 方法中使用 ReactDOM.createPortal 来创建一个全局的 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 可以让我们减少生成过多有副作用的节点,以往 render 必须返回单一节点,因此很多组件常常会产生过多无用的 divReact 根据这样的情况给予了一个组件来解决这个问题,它就是 Fragment

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

//or

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

函数组件以及 Hooks

Hooks 自去年10月发布以来,函数组件就派上了用场,React 的函数组件主要引用 SFC 返回(React.FunctionComponent),当然你也可以不引用 SFC 类型只不过返回的是(JSX.Element),这就是区别。

useState

以前:

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

现在:

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 被定义为一个泛型函数,因此类型可以由我们自己来指定。

useEffect

当你使用 useEffect 时,我们可以传入第三个参数来决定是否执行这个 callback ,这对于优化你的应用至关重要。

React.useEffect(() => {

}, [num]); 

useContext

对于 useContext 当你需要共享数据时可用:

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

如果你已经习惯 redux 不妨来看看 useReducer,假设我们需要通过按钮来更改文本颜色:

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

当我们需要来引用原生DOM来处理某件事情时,useRef 可以辅助我们完成这项工作:

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

接下来我们可以说一说 useMemo ,这只能当作一次性能优化的选择,通常情况下假设我们的 state 有两个属性,它的场景可能如下:

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>
    </>
  );
} 

                      

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap