React & Redux in TypeScript - 静态类型指南
2023-09-27 14:28:54 时间
最近在学习 react&TypeScript,发现有许多的组件模式和方法需要去梳理和总结。所以选择一些文章用于沉淀和思考,记录下一些知识点,和大家探讨。
目录:
- 简介,环境配置(create-react-app)
- React - 关键类型(react-redux-typescript-guide )
- React - 组件模式(react-redux-typescript-guide & TypeScript 2.8下的终极React组件模式)
- Redux - 使用以及 Redux Thunk 使用
- 总结
React - 关键类型
- 展示性组件(FunctionComponent)
- React.FunctionComponent<P> or React.FC<P>。
- const MyComponent: React.FC<Props> = ...
- 有状态组件(ClassComponent)
- React.Component<P, S>
- class MyComponent extends React.Component<Props, State> { ...
- 组件Props
- React.ComponentProps<typeof Component>
- 获取组件(适用于ClassComponent、FunctionComponent)的Props的类型
- type MyComponentProps = React.ComponentProps<typeof MyComponent>;
- React.FC | React.Component的联合类型
- React.ComponentType<P>
const withState = <P extends WrappedComponentProps>( WrappedComponent: React.ComponentType<P>, ) => { ...
- React 要素
- React.ReactElement<P> or JSX.Element
- 表示React元素概念的类型 - DOM组件(例如
- )或用户定义的复合组件()
- const elementOnly: React.ReactElement = <div /> || <MyComponent />;
- React Node
- React.ReactNode
- 表示任何类型的React节点(基本上是ReactElement(包括Fragments和Portals)+ 原始JS类型 的合集)
const elementOrPrimitive: React.ReactNode = 'string' || 0 || false || null || undefined || <div /> || <MyComponent />; const Component = ({ children: React.ReactNode }) => ...
- React CSS属性
- React.CSSProperties
- 代表着Style Object在 JSX 文件中(通常用于 css-in-js)
const styles: React.CSSProperties = { flexDirection: 'row', ... const element = <div style={styles} ...
- 通用的 React Event Handler
- React.ReactEventHandler<HTMLElement>
const handleChange: React.ReactEventHandler<HTMLInputElement> = (ev) => { ... } <input onChange={handleChange} ... />
- 特殊的 React Event Handler
- React.MouseEvent<E> | React.KeyboardEvent<E> | React.TouchEvent<E>
const handleChange = (ev: React.MouseEvent<HTMLDivElement>) => { ... } <div onMouseMove={handleChange} ... />
React 组件模式
- Function Components - FC 纯函数组件(无状态)
- 顾名思义,纯函数组件本身不具备 State,所以没有状态,一切通过 Props
import React, { FC, ReactElement, MouseEvent } from 'react' type Props = { label: string, children: ReactElement, onClick?: (e: MouseEvent<HTMLButtonElement>) => void } const FunctionComponent: FC<Props> = ({ label, children, onClick }: Props) => { return ( <div> <span> {label}: </span> <button type="button" onClick={onClick}> {children} </button> </div> ) } export default FunctionComponent
- 扩展属性(spread attributes)
- 利用 ... 对剩余属性进行处理
import React, { FC, ReactElement, MouseEvent, CSSProperties } from 'react' type Props = { label: string, children: ReactElement, className?: string, style?: CSSProperties, onClick?: (e: MouseEvent<HTMLButtonElement>) => void, } const FunctionComponent: FC<Props> = ({ label, children, onClick, ...resetProps }: Props) => { return ( <div {...resetProps}> <span>{label}:</span> <button type="button" onClick={onClick}> {children} </button> </div> ) } export default FunctionComponent
- 默认属性
- 如果,需要默认属性,可以通过默认参数值来处理
import React, { FC, ReactElement, MouseEvent } from 'react' type Props = { label?: string, children: ReactElement, } const FunctionComponent: FC<Props> = ({ label = 'Hello', children }: Props) => { return ( <div> <span> {label}: </span> <button type="button"> {children} </button> </div> ) } export default FunctionComponent
- Class Components
- 相对于FC,多了 state,采用如下形式来定义Class Component
这一部分的写法,与TypeScript 2.8下的终极React组件模式相同,觉得结构很清晰,复用。
import React, { Component } from 'react'; type Props = { label: string } const initialState = { count: 0 } type State = Readonly<typeof initialState> class ClassCounter extends Component<Props, State> { readonly state: State = initialState private handleIncrement = () => this.setState(Increment) render() { const { handleIncrement } = this; const { label } = this.props; const { count } = this.state; return ( <div> <span> {label}: {count} </span> <button type="button" onClick={handleIncrement}> {`Increment`} </button> </div> ) } } export const Increment = (preState: State) => ({ count: preState.count + 1 }) export default ClassCounter
- 默认属性
- 处理 Class Component 的默认属性,主要有两种方法:
- 一是定义高阶组件,例如TypeScript 2.8下的终极React组件模式中,利用 withDefaultProps 来定义默认属性,涉及组件的属性的类型转换;
- 二是利用 static props 以及 componentWillReceiveProps,处理默认属性。
- 具体业务中,视情况而定,第一中可以查看相关文章,这里介绍第二种
import React, { Component } from 'react'; type Props = { label: string, initialCount: number } type State = { count: number; } class ClassCounter extends Component<Props, State> { static defaultProps = { initialCount: 1, } // 依据 defaultProps 对 state 进行处理 readonly state: State = { count: this.props.initialCount, } private handleIncrement = () => this.setState(Increment) // 响应 defaultProps 的变化 componentWillReceiveProps({ initialCount }: Props) { if (initialCount != null && initialCount !== this.props.initialCount) { this.setState({ count: initialCount }) } } render() { const { handleIncrement } = this; const { label } = this.props; const { count } = this.state; return ( <div> <span> {label}: {count} </span> <button type="button" onClick={handleIncrement}> {`Increment`} </button> </div> ) } } export const Increment = (preState: State) => ({ count: preState.count + 1 }) export default ClassCounter
- 通用组件 Generic Components
- 复用共有的逻辑创建组件
- 常用于通用列表
import React, { Component, ReactElement } from 'react' interface GenericListProps<T> { items: T[], itemRenderer: (item: T, i: number) => ReactElement, } class GenericList<T> extends Component<GenericListProps<T>, {}> { render() { const { items, itemRenderer } = this.props return <div>{items.map(itemRenderer)}</div> } } export default GenericList
- Render Callback & Render Props
- Render Callback,也被称为函数子组件,就是将 children 替换为 () => children;
- Render Props,就是将 () => component 作为 Props 传递下去。
import React, { Component, ReactElement } from 'react'; type Props = { PropRender?: () => ReactElement, children?: () => ReactElement } class PropRender extends Component<Props, {}> { render() { const { props: { children, PropRender } }: { props: Props } = this; return ( <div> { PropRender && PropRender() } { children && children() } </div> ) } } export default PropRender // 应用 <PropsRender PropRender={() => (<p>Prop Render</p>)} > { () => (<p>Child Render</p>) } </PropsRender>
- HOC(Higher-Order Components)
- 简单理解为,接受React组件作为输入,输出一个新的React组件的组件的工厂函数。
import * as React from 'react' interface InjectedProps { label: string } export const withState = <BaseProps extends InjectedProps>( BaseComponent: React.ComponentType<BaseProps> ) => { type HocProps = BaseProps & InjectedProps & { initialCount?: number } type HocState = { readonly count: number } return class Hoc extends React.Component<HocProps, HocState> { // 方便 debugging in React-Dev-Tools static displayName = `withState(${BaseComponent.name})`; // 关联原始的 wrapped component static readonly WrappedComponent = BaseComponent; readonly state: HocState = { count: Number(this.props.initialCount) || 0, } handleIncrement = () => { this.setState({ count: this.state.count + 1 }) } render() { const { ...restProps } = this.props as any const { count } = this.state return ( <> {count} <BaseComponent onClick={this.handleIncrement} {...restProps} /> </> ) } } }
Redux - 使用以及 Redux Thunk 使用
以如下形式来介绍Redux,主要是in-ts的使用:
- (prestate, action) => state;
- 使用Redux Thunk 来出来异步操作。
// store.js type DataType = { counter: number } const DataState: DataType = { counter: 0 } type RootState = { Data: DataType } export default RootState // action.js import { Action, AnyAction } from 'redux' import { ThunkAction, ThunkDispatch } from 'redux-thunk' import RootState from '../store/index' type IncrementPayload = { value: number } interface IncrementAction extends Action { type: 'INCREMENT', payload: IncrementPayload } export const Increment = ({ value }: IncrementPayload): IncrementAction => { const payload = { value } return { type: 'INCREMENT', payload } } export type DecrementPayload = { value: number; }; export interface DecrementAction extends Action { type: 'DECREMENT'; payload: DecrementPayload; } export type RootAction = IncrementAction & DecrementAction; export const asyncIncrement = ( payload: IncrementPayload ): ThunkAction<Promise<void>, RootState, void, AnyAction> => { return async (dispatch: ThunkDispatch<RootState, void, AnyAction>): Promise<void> => { return new Promise<void>((resolve) => { console.log('Login in progress') setTimeout(() => { dispatch(Increment(payload)) setTimeout(() => { resolve() }, 1000) }, 3000) }) } } // reducer.js import { DataState, DataType } from '../store/Data' import { RootAction } from '../actions/' export default function (state: DataType = DataState, { type, payload }: RootAction): DataType { switch(type) { case 'INCREMENT': return { ...state, counter: state.counter + payload.value, }; default: return state; } } // Hearder.js import React, { Component, ReactNode } from 'react' import RootState from '../store/index' import { Dispatch, AnyAction } from 'redux' import { ThunkDispatch } from 'redux-thunk' import { connect } from 'react-redux' import { Increment, asyncIncrement } from '../actions/' const initialState = { name: 'string' } type StateToPropsType = Readonly<{ counter: number }> type DispatchToPropsType = Readonly<{ handleAdd: () => void, handleDec: () => void }> type StateType = Readonly<typeof initialState> type PropsType = { children?: ReactNode } type ComponentProps = StateToPropsType & DispatchToPropsType & PropsType class Header extends Component<ComponentProps, StateType> { readonly state: StateType = initialState; render() { const { props: { handleAdd, handleDec, counter }, state: { name } } = this return ( <div> 计数:{counter} <button onClick={handleAdd}>+</button> <button onClick={handleDec}>-</button> </div> ) } private handleClick = () => this.setState(sayHello); } const sayHello = (prevState: StateType) => ({ name: prevState.name + 'Hello world', }) const mapStateToProps = (state: RootState, props: PropsType): StateToPropsType => { return { counter: state.Data.counter } } const mapDispatchToProps = (dispatch: ThunkDispatch<RootState, void, AnyAction>): DispatchToPropsType => { return { handleAdd: () => { dispatch(Increment({ value: 2 })) }, handleDec: async () => { dispatch(asyncIncrement({ value: 10 })) } } } export default connect< StateToPropsType, DispatchToPropsType, PropsType, RootState> (mapStateToProps, mapDispatchToProps)(Header)
相关文章
- 4.前端注册表单验证 && 表单回填
- DataSet & DataTable &DataRow 深入浅出
- 【微信小程序】--注册小程序账号&安装开发者工具(一)
- net.ifnames & biosdevname
- HUAWEI USG6000系列 & NGFW Module V100R001 典型配置案例
- 一文足矣:Unity&行为树
- Maven Learning - Direct Dependencies & Transitive Dependencies
- Simple TCP/IP Echo Server & Client Application in C#
- SAP 盘盈盘亏移动类型701&702 Vs 711&712
- ar 构建静态库&动态库
- xml & 编码
- flink table&sql canal使用案例
- STM32驱动AHT10&OLED显示温湿度
- 「高频必考」Docker&K8S面试题和答案
- android4.4系统解决“ERRORcouldn't find native method”方法
- CentOS 7拨号上网(ADSL & PPPoE)
- 学习笔记(14):Python网络编程&并发编程-文件传输功能实现
- Shell - type & which
- 15.0 vue3 provide&inject跨组件通信方式