在React项目中使用TypeScript详情

目录
  • 项目目录及ts文件划分
  • 在项目中使用TypeScript具体实践
    • 组件声明
  • React Hooks使用
    • useState
    • useRef
    • useCallback
    • useMemo
    • useContext
    • useReducer
    • useImperativeHandle
    • Axios请求/响应定义封装

前言:

本文主要记录我如何在React项目中优雅的使用TypeScript,来提高开发效率及项目的健壮性。

项目目录及ts文件划分

由于我在实际项目中大部分是使用umi来进行开发项目,所以使用umi生成的目录来做案例。

.
├── README.md
├── global.d.ts
├── mock
├── package.json
├── src
│   ├── assets
│   ├── components
│   │   └── PublicComA
│   │       ├── index.d.ts
│   │       ├── index.less
│   │       └── index.tsx
│   ├── layouts
│   ├── models
│   ├── pages
│   │   ├── PageA
│   │   │   ├── index.d.ts
│   │   │   ├── index.less
│   │   │   └── index.tsx
│   │   ├── index.less
│   │   └── index.tsx
│   └── utils
├── tsconfig.json
├── typings.d.ts
└── yarn.lock

在项目根目录下有typings.d.ts和global.d.ts这两个文件, 前者我们可以放置一些全局的导出模块,比如css,less, 图片的导出声明;后者可以放一些全局声明的变量, 接口等, 比如说window下全局变量的声明等。

如下:

// typings.d.ts
declare module '*.css';
declare module '*.less';
declare module "*.png";
declare module "*.jpeg";
declare module '*.svg' {
  export function ReactComponent(props: React.SVGProps<SVGSVGElement>): React.ReactElement
  const url: string
  export default url
}
// global.d.ts
interface Window {
  helloWorld: () => void;
}

接下来介绍一下src目录:

  • assets 存放静态资源如图片/视频/音频等, 参与webpack的打包过程
  • layouts 存放公共布局
  • components 存放全局公共组件
  • models dva的models文件夹
  • pages 存放页面的目录, 内部可以有页面组件components, 结构类似于全局的components
  • utils 存放js工具库, 请求库等公共js文件

在pages和components中有存放当前组件/页面所需要的类型和接口声明的index.d.ts。另外如models中的文件由于是每个model私有类型和接口声明,所以可以直接在文件内部去声明。 具体的目录规划如上,可以根据实际项目来做更合理的划分。

在项目中使用TypeScript具体实践

组件声明

  • 函数组件 推荐使用React.FC<P={}>来表示函数类型,当使用该类型定义组件时,props中会默认带有children属性。
interface IProps {
  count: number
}

const App: React.FC<IProps> = (props) => {
  const {count} = props;
  return (
    <div className="App">
      <span>count: {count}</span>
    </div>
  );
}
  • 类组件 类组件接受两个参数,第一个是props的定义,第二个是state的定义,如果使用React.PureComponent<P, S={} SS={}>定义组件,则还有第三个参数,表示getSnapshotBeforeUpdate的返回值。
interface IProps {
  name: string;
}

interface IState {
  count: number;
}

class App extends React.Component<IProps, IState> {
  state = {
    count: 0
  };
  render() {
    return (
      <div>
        {this.state.count}
        {this.props.name}
      </div>
    );
  }
}

React Hooks使用

useState

声明定义:

function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
// convenience overload when first argument is omitted
	/**
	 * Returns a stateful value, and a function to update it.
   *
   * @version 16.8.0
   * @see https://reactjs.org/docs/hooks-reference.html#usestate
   */

function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
  /**
   * An alternative to `useState`.
   *
   * `useReducer` is usually preferable to `useState` when you have complex state logic that involves
   * multiple sub-values. It also lets you optimize performance for components that trigger deep
   * updates because you can pass `dispatch` down instead of callbacks.
   *
   * @version 16.8.0
   * @see https://reactjs.org/docs/hooks-reference.html#usereducer
   */

如果初始值能够体现出类型,那么可以不用手动声明类型,TS会自动推断出类型。如果初始值为null或者undefined则需要通过泛型显示声明类型。

如下:

const [count, setCount] = useState(1);
const [user, setUser] = useState<IUser | null>(null);

useRef

声明定义:

 function useRef<T>(initialValue: T): MutableRefObject<T>;
 // convenience overload for refs given as a ref prop as they typically start with a null value
 /**
   * `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
   * (`initialValue`). The returned object will persist for the full lifetime of the component.
   *
   * Note that `useRef()` is useful for more than the `ref` attribute. It's handy for keeping any mutable
   * value around similar to how you'd use instance fields in classes.
   *
   * Usage note: if you need the result of useRef to be directly mutable, include `| null` in the type
   * of the generic argument.
   *
   * @version 16.8.0
   * @see https://reactjs.org/docs/hooks-reference.html#useref
   */

使用该Hook时,要根据使用场景来判断传入泛型类型,如果是获取DOM节点,则传入对应DOM类型即可;如果需要的是一个可变对象,则需要在泛型参数中包含'| null'。

如下:

// 不可变DOM节点,只读
const inputRef = useRef<HTMLInputElement>(null);

// 可变,可重新复制
const idRef = useRef<string | null>(null);
idRef.current = "abc";

useCallback

声明定义:

 function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;
 /**
  * `useMemo` will only recompute the memoized value when one of the `deps` has changed.
  *
  * Usage note: if calling `useMemo` with a referentially stable function, also give it as the input in
  * the second argument.
  *
  * ```ts
  * function expensive () { ... }
  *
  * function Component () {
  *   const expensiveResult = useMemo(expensive, [expensive])
  *   return ...
  * }
  * ```
  *
  * @version 16.8.0
  * @see https://reactjs.org/docs/hooks-reference.html#usememo
  */

useCallback会根据返回值自动推断出类型,如果传入的参数不指定类型,则会默认为any,所以为了严谨和可维护性,一定要指定入参的类型。也可以手动传入泛型指定函数类型。

如下:

// 会自动推导出类型: (a: number, b: number) => number;
const add = useCallback((a: number, b: number) => a + b, [a, b])

// 传入泛型,则指定函数类型
const toggle = useCallback<(a: number) => number>((a: number) => a * 2, [a])

useMemo

声明定义:

function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
   /**
    * `useDebugValue` can be used to display a label for custom hooks in React DevTools.
    *
    * NOTE: We don't recommend adding debug values to every custom hook.
    * It's most valuable for custom hooks that are part of shared libraries.
    *
    * @version 16.8.0
    * @see https://reactjs.org/docs/hooks-reference.html#usedebugvalue
    */

useMemo和useCallback类似,只是定义类型为具体返回值的类型,而不是函数的类型。

如下:

// 会自动推导出类型: number;
const add = useCallback((a: number, b: number) => a + b, [a, b])

// 传入泛型,则指定函数类型
const toggle = useCallback<number>((a: number) => a * 2, [a])

useContext

声明定义:

function useContext<T>(context: Context<T>/*, (not public API) observedBits?: number|boolean */): T;
/**
  * Returns a stateful value, and a function to update it.
  *
  * @version 16.8.0
  * @see https://reactjs.org/docs/hooks-reference.html#usestate
  */

useContext会根据传入的上下文对象自动推导出context的类型,当然也可以使用泛型来设置context的类型,

如下:

interface ITheme {
	color: string;
}
const ThemeContext = React.createContext<ITheme>({ color: "red" });

// 自动推导出类型为ITheme
const theme = useContext(ThemeContext); // 等同于const theme = useContext<ITheme>(ThemeContext);

useReducer

声明定义:

function useReducer<R extends Reducer<any, any>>(
    reducer: R,
    initialState: ReducerState<R>,
    initializer?: undefined
): [ReducerState<R>, Dispatch<ReducerAction<R>>];
/**
  * `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
  * (`initialValue`). The returned object will persist for the full lifetime of the component.
  *
  * Note that `useRef()` is useful for more than the `ref` attribute. It's handy for keeping any mutable
  * value around similar to how you'd use instance fields in classes.
  *
  * @version 16.8.0
  * @see https://reactjs.org/docs/hooks-reference.html#useref
  */

上面只列出了一种类型定义,我在项目中也是使用这种定义去指定useReducer的类型。普通的案例如下:

type StateType = {
  name: string;
  age: number;
}

type Actions = {
  type: 'Change_Name';
  payload: string;
} | {
  type: 'Change_Age';
  payload: number;
}

const initialState = {
  name: '小明',
  age: 18
}

const reducerAction: Reducer<StateType, Actions> = (
  state,
  action,
) => {
  switch (action.type) {
    case 'Change_Name':
      return { ...state, name: action.payload };
    case 'Change_Age':
      return { ...state, age: action.payload };
    default:
      return state;
  }
};

function Index() {
  const [state, dispatch] = useReducer(reducerAction, initialState);
  return (
    <div>
      <div>姓名:{state.name}</div>
      <div>年龄:{state.age}</div>
    </div>
  );
}

可以看到,这样能够得到正确的类型推断,但是略微繁琐。

案例如下:

// 定义一个生成Action类型的泛型
type ActionMap<M extends Record<string, any>> = {
  [Key in keyof M]: M[Key] extends undefined
    ? {
        type: Key
      }
    : {
        type: Key
        payload: M[Key]
      }
}

type StateType = {
  name: string;
  age: number;
}

// 定义具体的Action类型
type PayloadType = {
  Change_Name: string;
  Change_Age: number;
}

/**
  ActionMap<PayloadType>会生成类型
  {
    Change_Name: {
        type: Types.Name;
        payload: string;
    };
    Change_Age: {
        type: Types.Age;
        payload: number;
    };
  }
  而keyof ActionMap<PayloadType>则会生成 'Change_Name' | 'Change_Age'的类型。
  所以Action最终的类型便为:
  type Actions = {
      type: Types.Name;
      payload: string;
  } | {
      type: Types.Age;
      payload: number;
  }
*/
type Actions = ActionMap<PayloadType>[keyof ActionMap<PayloadType>]

const initialState = {
  name: '小明',
  age: 18
}

const reducerAction: Reducer<StateType, Actions> = (
  state,
  action,
) => {
  switch (action.type) {
    case Types.Name:
      return { ...state, name: action.payload };
    case Types.Age:
      return { ...state, age: action.payload };
    default:
      return state;
  }
};

我们定义了一个ActionMap泛型,该泛型会将传入的类型{key: value}生成为新的{key: {type: key, payload: value }类型。然后我们利用keyof关键字获取到所有的key,就可以得到我们所需要的{type: key1, payload: value1} | {type: key2, payload: value2}的类型了。只要我们定义好PayloadType类型,则可以自动推导出我们需要的Actions类型。

useImperativeHandle

声明定义:

  function useImperativeHandle<T, R extends T>(ref: Ref<T>|undefined, init: () => R, deps?: DependencyList): void;
  // NOTE: this does not accept strings, but this will have to be fixed by removing strings from type Ref<T>
  /**
   * `useImperativeHandle` customizes the instance value that is exposed to parent components when using
   * `ref`. As always, imperative code using refs should be avoided in most cases.
   *
   * `useImperativeHandle` should be used with `React.forwardRef`.
   *
   * @version 16.8.0
   * @see https://reactjs.org/docs/hooks-reference.html#useimperativehandle
   */

useImperativeHandle可以让自定义组件通过ref属性,将内部属性暴露给父组件进行访问。因为是函数式组件,所以需要结合forwardRef一起使用。

案例如下:

interface FancyProps {}

interface FancyRef {
  focus: () => void;
}
const FancyInput = forwardRef<FancyRef, FancyProps>((props, ref) => {
  const inputRef = useRef<HTMLInputElement>(null);
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current?.focus();
    }
  }));
  return (
    <input ref={inputRef} {...props} />
  );
})
const Parent = () => {
  // 定义子组件ref
  const inputRef = useRef<FancyRef>(null);
  return (
    <div>
      <FancyInput
        ref={inputRef}
      />
      <button
        onClick={() => {
          // 调用子组件方法
          inputRef.current?.focus();
        }}
      >聚焦</button>
    </div>
  )
}

Axios请求/响应定义封装

axios是很流行的http库,他的ts封装已经很完美了,我们只做简单的二次封装,返回通用的数据响应格式。 首先在utils/request.ts中创建一个构造axios实例的生成器:

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';

// 拦截器定义
export interface RequestInterceptors {
  // 请求拦截
  requestInterceptors?: (config: AxiosRequestConfig) => AxiosRequestConfig
  requestInterceptorsCatch?: (err: any) => any
  // 响应拦截
  responseInterceptors?: (config: AxiosResponse) => AxiosResponse
  responseInterceptorsCatch?: (err: any) => any
}

// 生成axios实例的参数,实例可以单独传入拦截器
export interface RequestConfig extends AxiosRequestConfig {
  interceptorsObj?: RequestInterceptors
}

// loading请求数量
let loadingCount: number = 0;

// 打开loading
const showLoading = () => {
  loadingCount ++;
  if(loadingCount > 0) {
    // 显示loading
    // Loading.show()
  }
}

// 关闭loading
const hideLoading = () => {
  loadingCount --;
  if(loadingCount <= 0) {
    // 隐藏loading
    // Loading.hide();
  }
}

function RequestBuilder(config: RequestConfig) {
  const { interceptorsObj, ...res } = config;

  const instance: AxiosInstance = axios.create(res);

  // 全局请求拦截器
  instance.interceptors.request.use(
    (request: AxiosRequestConfig) => {
      // 显示loading
      showLoading();
      console.log('全局请求拦截器');
      // TODO:全局的请求头操作等等
      return request;
    },
    (err: any) => err,
  )

  /**
   * 实例请求拦截器
   * 要注意 axios请求拦截器为倒序执行,所以要将实例请求拦截器注册在全局请求拦截器后面
   */
  instance.interceptors.request.use(
    interceptorsObj?.requestInterceptors,
    interceptorsObj?.requestInterceptorsCatch,
  )

  /**
   * 实例响应拦截器
   * axios响应拦截器为正序执行,所以要将实例响应拦截器注册在全局响应拦截器前面
   */
  instance.interceptors.response.use(
    interceptorsObj?.responseInterceptors,
    interceptorsObj?.responseInterceptorsCatch,
  )

  // 全局响应拦截器
  instance.interceptors.response.use(
    (response: AxiosResponse) => {
      console.log('全局响应拦截器');
      // 关闭loading
      hideLoading();
      // TODO: 通用的全局响应处理,token过期重定向登录等等
      // 返回值为res.data,即后端接口返回的数据,减少解构的层级,以及统一响应数据格式。
      return response.data
    },
    (err: any) => {
      // 关闭loading
      hideLoading();
      // TODO: 错误提示等
      return err;
    },
  )

  return instance;
}
export const http = RequestBuilder({baseURL: '/api'});

该生成器可以实现每个实例有单独的拦截器处理逻辑,并且实现全局的loading加载效果,全局拦截器的具体实现可以根据项目实际需求进行填充。生成器已经完成,但是还没法定制我们的通用响应数据,接下来我们在typings.d.ts中重新定义axios模块:

import * as axios from 'axios';
declare module 'axios' {
  // 定制业务相关的网络请求响应格式, T 是具体的接口返回类型数据
  export interface CustomSuccessData<T> {
    code: number;
    msg?: string;
    message?: string;
    data: T;
    [keys: string]: any;
  }
  export interface AxiosInstance {
    // <T = any>(config: AxiosRequestConfig): Promise<CustomSuccessData<T>>;
    request<T = any, R = CustomSuccessData<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
    get<T = any, R = CustomSuccessData<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
    delete<T = any, R = CustomSuccessData<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
    head<T = any, R = CustomSuccessData<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
    post<T = any, R = CustomSuccessData<T>, D = any>(
      url: string,
      data?: D,
      config?: AxiosRequestConfig<D>,
    ): Promise<R>;
    put<T = any, R = CustomSuccessData<T>, D = any>(
      url: string,
      data?: D,
      config?: AxiosRequestConfig<D>,
    ): Promise<R>;
    patch<T = any, R = CustomSuccessData<T>, D = any>(
      url: string,
      data?: D,
      config?: AxiosRequestConfig<D>,
    ): Promise<R>;
  }
}

完成以上操作后,我们在业务代码中具体使用:

import { http } from '@/utils/request';

interface Req {
  userId: string;
}

interface Res {
  userName: string;
  userId: string;
}

// 获取用户信息接口
const getUserInfo = async (params: Req) => {
  return http.get<Res>('/getUserInfo', {params})
}

这个时候getUserInfo返回的就是CustomSuccessData<Res>类型的数据了。至此我们对axios简单的封装也就完成了。

到此这篇关于在React项目中使用TypeScript详情的文章就介绍到这了,更多相关React使用TypeScript内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Typescript 封装 Axios拦截器方法实例

    目录 引言 创建 class axios.create([config]) 封装 request(config)通用方法 封装-拦截器(单个实例独享) 扩展 Http 自定义拦截器 封装-拦截器(所有实例共享) 封装-拦截器(单个请求独享) 装修 Http class 返回经过 request 返回数据结构(DTO) 拦截器执行顺序 操作场景控制 引言 对 axios 二次封装,更加的可配置化.扩展性更加强大灵活 通过 class 类实现,class 具备更强封装性(封装.继承.多态),通过实例

  • 前端算法之TypeScript包含min函数的栈实例详解

    目录 前言 思路梳理 实现代码 示例代码 前言 基于数据结构: “栈”,实现一个min函数,调用此函数即可获取栈中的最小元素.在该栈中,调用min.push.pop的时间复杂度都是O(1). 本文就跟大家分享下这个算法,欢迎各位感兴趣的开发者阅读本文. 思路梳理 相信大多数开发者看到这个问题,第一反应可能是每次往栈中压入一个新元素时,将栈里的所有元素排序,让最小的元素位于栈顶,这样就能在O(1)的时间内得到最小元素了.但这种思路不能保证最后入栈的元素能够最先出栈,因此这个思路行不通. 紧接着,我

  • TypeScript数组实现栈与对象实现栈的区别详解

    目录 前言 数组实现栈 实现思路 实现代码 编写测试代码 对象实现栈 实现代码 编写测试代码 二者的区别 十进制转二进制 前言 栈作为一种数据结构,它可以应用在很多地方,当你需要经常获取刚存放进去的数据时,那么栈这种数据结构将是你的首选. 栈的实现方式一般有两种:数组实现和对象实现,这两种实现方式最终实现的功能都是一样的,但是在性能上却有着很大的差别. 本文将详细讲解这两种实现方式的差异并用TypeScript将其实现,欢迎各位感兴趣的开发者阅读本文. 数组实现栈 本文讲解的是栈用代码的实现,如

  • TypeScript判断对称的二叉树方案详解

    目录 前言 实现思路 实现代码 示例代码 前言 如果一颗二叉树和它的镜像一样,那么它就是对称的.实现一个函数用于判断一颗二叉树是否对称,你会怎么做? 本文将分享一种解决方案,欢迎各位感兴趣的开发者阅读本文. 实现思路 在上一篇文章二叉树的镜像中我们知道了此问题的解决方案是前序遍历,那么我们可以修改下前序遍历算法,父节点遍历后,先遍历它的右子节点,再遍历它的左子节点,我们把这种算法称为:对称前序遍历 如下图所示的两棵树,我们分别列举下两种遍历的结果: 树A: 前序遍历:8, 6, 5, 7, 6,

  • TypeScript获取二叉树的镜像实例

    目录 前言 思路分析 实现代码 前言 给定一颗二叉树,如何获取它的镜像?本文将跟大家分享这个问题的解决方案,欢迎各位感兴趣的开发者阅读本文. 思路分析 当我们把一张写有文字的纸放在镜子前面,你看到的内容正好与你写的内容是相反的.那么我们就可以依据照镜子的经验画出它的镜像了,如下所示: 镜像前后的两棵树根节点相同 镜像后的树与镜像前相比:它们的左.右子节点交换了位置 通过观察后,我们就得出了一颗树的镜像过程:先序遍历这棵树的每个节点,如果遍历到的节点有子节点,就交换它的两个子节点.当交换完所有非叶

  • Zod进行TypeScript类型验证使用详解

    目录 引言 什么是类型验证,为什么需要它? 为什么要使用zod? 使用 Zod 进行类型验证的示例 Primitives 对象 类型推断 组合类型 注意事项 安全解析 无法识别的Key被删除 其他事项 Zod 与其他库的比较 结论 引言 这篇文章将描述如何使用Zod为您的项目设置类型验证.Zod 是一个用于类型声明和验证的开源 TypeScript 库.我们将研究为什么使用 Zod 进行类型验证,提供如何使用它的示例,并将其与其他库进行比较. 什么是类型验证,为什么需要它? 类型验证是验证数据结

  • 在React项目中使用TypeScript详情

    目录 项目目录及ts文件划分 在项目中使用TypeScript具体实践 组件声明 React Hooks使用 useState useRef useCallback useMemo useContext useReducer useImperativeHandle Axios请求/响应定义封装 前言: 本文主要记录我如何在React项目中优雅的使用TypeScript,来提高开发效率及项目的健壮性. 项目目录及ts文件划分 由于我在实际项目中大部分是使用umi来进行开发项目,所以使用umi生成的

  • React项目中应用TypeScript的实现

    目录 一.前言 二.使用方式 无状态组件 有状态组件 受控组件 三.总结 一.前言 单独的使用typescript 并不会导致学习成本很高,但是绝大部分前端开发者的项目都是依赖于框架的 例如和vue.react 这些框架结合使用的时候,会有一定的门槛 使用 TypeScript 编写 react 代码,除了需要 typescript 这个库之外,还需要安装@types/react.@types/react-dom npm i @types/react -s npm i @types/react-

  • TypeScript在React项目中的使用实践总结

    序言 本文会侧重于TypeScript(以下简称TS)在项目中与React的结合使用情况,而非TS的基本概念.关于TS的类型查看可以使用在线TS工具

  • 详解React项目中eslint使用百度风格

    1.安装百度Eslint Rule 插件 npm i -D eslint @babel/eslint-parser @babel/eslint-plugin @ecomfe/eslint-config // react项目 npm i -D eslint-plugin-react eslint-plugin-react-hooks // 如果需要支持typescript的话 npm i -D @typescript-eslint/parser @typescript-eslint/eslint-

  • 详解如何优雅地在React项目中使用Redux

    前言 或许你当前的项目还没有到应用Redux的程度,但提前了解一下也没有坏处,本文不会安利大家使用Redux 概念 首先我们会用到哪些框架和工具呢? React UI框架 Redux 状态管理工具,与React没有任何关系,其他UI框架也可以使用Redux react-redux React插件,作用:方便在React项目中使用Redux react-thunk 中间件,作用:支持异步action 目录结构 Tips:与Redux无关的目录已省略 |--src |-- store Redux目录

  • 优雅的在React项目中使用Redux的方法

    或许你当前的项目还没有到应用Redux的程度,但提前了解一下也没有坏处 首先我们会用到哪些框架和工具呢? React UI框架 Redux 状态管理工具,与React没有任何关系,其他UI框架也可以使用Redux react-redux React插件,作用:方便在React项目中使用Redux react-thunk 中间件,作用:支持异步action |--src |-- store Redux目录 |-- actions.js |-- index.js |-- reducers.js |-

  • 在Vue项目中使用Typescript的实现

    3.0迟迟没有发布release版本,现阶段在Vue项目中使用Typescript需要花不小的精力在工程的配置上面.主要的工作是webpack对TS,TSX的处理,以及2.x版本下面使用class的形式书写Vue 组件的一些限制和注意事项. Webpack 配置 配置webpack对TS,TSX的支持,以便于我们在Vue项目中使用Typescript和tsx. module.exports = { entry: './index.vue', output: { filename: 'bundle

  • 在react项目中使用antd的form组件,动态设置input框的值

    问题: 创建账号时,输入账号后不搜索直接保存,提示查询后,再点搜索就不能搜索这个账号了 原因: 点击保存之后,对表单进行了验证,导致之后请求的数据无法在更新到input框中,也就是说即使在state中有值,也不会更新initialValue值,就导致搜索后的值不能正确填入input中,表单也就提交不了. 解决办法: 不使用initialValue设置动态更新的值,而是使用 this.props.form.setFieldValue({name:data}); 用于动态更新值,就可以解决了. if

  • react 项目中引入图片的几种方式

    img标签引入图片 因为react其实是通过js的reader函数渲染的页面,所以直接写src="路径"是无法引入图片 我们可以像引入模块一样引入图片 import img from './../../../../asset/img/user.png' 需要用下面的方式引入 <img src={require('../images/picture.png')} alt="标签"/> 背景图片引入 1 第一种就是常规的 新建一个css文件,然后就可以直接写

  • 如何在Vue项目中应用TypeScript类

    目录 一.前言 二.使用 1.@Component 2.compued.data.methods 3.@props 4.@watch 5.@emit 三 .总结 一.前言 TypeScript是基于vue-class-component库而来,这个库vue官方推出的一个支持使用class方式来开发vue单文件组件的库 主要的功能如下: methods 可以直接声明为类的成员方法 计算属性可以被声明为类的属性访问器 初始化的 data 可以被声明为类属性 data.render 以及所有的 Vue

随机推荐