返回介绍

二、React 项目实践

发布于 2024-09-07 18:09:18 字数 12732 浏览 0 评论 0 收藏 0

2.1 手动创建 react 项目

项目代码 https://github.com/poetries/typescript-in-action/tree/master/ts-react

1. 安装依赖文件

yarn add @types/react @types/react-dom

2. 修改 tsconfig.json 配置

修改 compilerOptions 中的 jsxreact

2.2 使用脚手架安装

项目代码 https://github.com/poetries/typescript-in-action/tree/master/ts-react-app

create-react-app ts-react-app --typescript

2.2.1 函数组件

import React from 'react';
import { Button } from 'antd';

interface Greeting {
  name: string;
  firstName: string;
  lastName: string;
}

const Hello = (props: Greeting) => <Button>Hello {props.name}</Button>

// const Hello: React.FC<Greeting> = ({
//   name,
//   firstName,
//   lastName,
//   children
// }) => <Button>Hello {name}</Button>

Hello.defaultProps = {
  firstName: '',
  lastName: ''
}

export default Hello;

2.2.2 类组件

import React, { Component } from 'react';
import { Button } from 'antd';

interface Greeting {
  name: string;
  firstName?: string;
  lastName?: string;
}

interface HelloState {
  count: number
}

class HelloClass extends Component<Greeting, HelloState> {
  state: HelloState = {
    count: 0
  }
  static defaultProps = {
    firstName: '',
    lastName: ''
  }
  render() {
    return (
      <>
        <p>你点击了 {this.state.count} 次</p>
        <Button onClick={() => {this.setState({count: this.state.count + 1})}}>
          Hello {this.props.name}
        </Button>
      </>
    )
  }
}

export default HelloClass;

2.2.3 高阶组件

import React, { Component } from 'react';

import HelloClass from './HelloClass';

interface Loading {
  loading: boolean
}

function HelloHOC<P>(WrappedComponent: React.ComponentType<P>) {
  return class extends Component<P & Loading> {
    render() {
      const { loading, ...props } = this.props;
      return loading ? <div>Loading...</div> : <WrappedComponent { ...props as P } />;
    }
  }
}

export default HelloHOC(HelloClass);

2.2.4 Hooks 组件

import React, { useState, useEffect } from 'react';
import { Button } from 'antd';

interface Greeting {
  name: string;
  firstName: string;
  lastName: string;
}

const HelloHooks = (props: Greeting) => {
  const [count, setCount] = useState(0);
  const [text, setText] = useState<string | null>(null);

  useEffect(() => {
    if (count > 5) {
      setText('休息一下');
    }
  }, [count]);

  return (
    <>
      <p>你点击了 {count} 次 {text}</p>
      <Button onClick={() => {setCount(count + 1)}}>
        Hello {props.name}
      </Button>
    </>
  )
}

HelloHooks.defaultProps = {
  firstName: '',
  lastName: ''
}

export default HelloHooks;

2.2.5 事件处理与数据请求

import React, { Component, useState, useEffect } from 'react';
import { Form, Input, Select, Button } from 'antd';
import { FormComponentProps } from 'antd/lib/form';

import { get } from '../../utils/request';
import { GET_EMPLOYEE_URL } from '../../constants/urls';
import { EmployeeRequest, EmployeeResponse } from '../../interface/employee';

const { Option } = Select;

interface Props extends FormComponentProps {
  onDataChange(data: EmployeeResponse): void
}

// Hooks version
// const QueryFormHooks = (props: Props) => {
//   const [name, setName] = useState('');
//   const [departmentId, setDepartmentId] = useState<number | undefined>();

//   const handleNameChange = (e: React.FormEvent<HTMLInputElement>) => {
//     setName(e.currentTarget.value)
//   }

//   const handleDepartmentChange = (value: number) => {
//     setDepartmentId(value)
//   }

//   const handleSubmit = () => {
//     queryEmployee({name, departmentId});
//   }

//   const queryEmployee = (param: EmployeeRequest) => {
//     get(GET_EMPLOYEE_URL, param).then(res => {
//       props.onDataChange(res.data);
//     });
//   }

//   useEffect(() => {
//     queryEmployee({name, departmentId});
//   }, [])

//   return (
//     <>
//       <Form layout="inline">
//         <Form.Item>
//           <Input
//             placeholder="姓名"
//             style={{ width: 120 }}
//             allowClear
//             value={name}
//             onChange={handleNameChange}
//           />
//         </Form.Item>
//         <Form.Item>
//         <Select
//           placeholder="部门"
//           style={{ width: 120 }}
//           allowClear
//           value={departmentId}
//           onChange={handleDepartmentChange}
//         >
//           <Option value={1}>技术部</Option>
//           <Option value={2}>产品部</Option>
//           <Option value={3}>市场部</Option>
//           <Option value={4}>运营部</Option>
//         </Select>
//         </Form.Item>
//         <Form.Item>
//           <Button type="primary" onClick={handleSubmit}>查询</Button>
//         </Form.Item>
//       </Form>
//     </>
//   )
// }

class QueryForm extends Component<Props, EmployeeRequest> {
  state: EmployeeRequest = {
    name: '',
    departmentId: undefined
  }
  handleNameChange = (e: React.FormEvent<HTMLInputElement>) => {
    this.setState({
      name: e.currentTarget.value
    });
  }
  handleDepartmentChange = (value: number) => {
    this.setState({
      departmentId: value
    });
  }
  handleSubmit = () => {
    this.queryEmployee(this.state);
  }
  componentDidMount() {
    this.queryEmployee(this.state);
  }
  queryEmployee(param: EmployeeRequest) {
    get(GET_EMPLOYEE_URL, param).then(res => {
      this.props.onDataChange(res.data);
    });
  }
  render() {
    return (
      <Form layout="inline">
        <Form.Item>
          <Input
            placeholder="姓名"
            style={{ width: 120 }}
            allowClear
            value={this.state.name}
            onChange={this.handleNameChange}
          />
        </Form.Item>
        <Form.Item>
        <Select
          placeholder="部门"
          style={{ width: 120 }}
          allowClear
          value={this.state.departmentId}
          onChange={this.handleDepartmentChange}
        >
          <Option value={1}>技术部</Option>
          <Option value={2}>产品部</Option>
          <Option value={3}>市场部</Option>
          <Option value={4}>运营部</Option>
        </Select>
        </Form.Item>
        <Form.Item>
          <Button type="primary" onClick={this.handleSubmit}>查询</Button>
        </Form.Item>
      </Form>
    )
  }
}

const WrapQueryForm = Form.create<Props>({
  name: 'employee_query'
})(QueryForm);

export default WrapQueryForm;

2.2.6 列表渲染

import React, { Component, useState } from 'react';
import { Table } from 'antd';

import './index.css';

import QueryForm from './QueryForm';

import { employeeColumns } from './colums';
import { EmployeeResponse } from '../../interface/employee';

// Hooks version
// const Employee = () => {
//   const [employee, setEmployee] = useState<EmployeeResponse>(undefined);

//   const getTotal = () => {
//     let total: number;
//     if (typeof employee !== 'undefined') {
//       total = employee.length
//     } else {
//       total = 0
//     }
//     return <p>共 {total} 名员工</p>
//   }

//   return (
//     <>
//       <QueryForm onDataChange={setEmployee} />
//       {/* {getTotal()} */}
//       <Table columns={employeeColumns} dataSource={employee} className="table" />
//     </>
//   )
// }

interface State {
  employee: EmployeeResponse
}

class Employee extends Component<{}, State> {
  state: State = {
    employee: undefined
  }
  setEmployee = (employee: EmployeeResponse) => {
    this.setState({
      employee
    });
  }
  getTotal() {
    let total: number;
    // 类型保护
    if (typeof this.state.employee !== 'undefined') {
      total = this.state.employee.length
    } else {
      total = 0
    }
    return <p>共 {total} 名员工</p>
  }
  render() {
    return (
      <>
        <QueryForm onDataChange={this.setEmployee} />
        {/* {this.getTotal()} */}
        <Table columns={employeeColumns} dataSource={this.state.employee} className="table" />
      </>
    )
  }
}

export default Employee;

2.2.7 Redux 与类型

项目代码 https://github.com/poetries/typescript-in-action/tree/master/ts-redux

import { Dispatch } from 'redux';
import _ from 'lodash';

import { get, post } from '../../utils/request';
import { department, level } from '../../constants/options';

import {
  GET_EMPLOYEE_URL,
  CREATE_EMPLOYEE_URL,
  DELETE_EMPLOYEE_URL,
  UPDATE_EMPLOYEE_URL
} from '../../constants/urls';

import {
  GET_EMPLOYEE,
  CREATE_EMPLOYEE,
  DELETE_EMPLOYEE,
  UPDATE_EMPLOYEE
} from '../../constants/actions';

import {
  EmployeeInfo,
  EmployeeRequest,
  EmployeeResponse,
  CreateRequest,
  DeleteRequest,
  UpdateRequest
} from '../../interface/employee';

type State = Readonly<{
  employeeList: EmployeeResponse
}>

type Action = {
  type: string;
  payload: any;
}

const initialState: State = {
  employeeList: undefined
}

export function getEmployee(param: EmployeeRequest, callback: () => void) {
  return (dispatch: Dispatch) => {
    get(GET_EMPLOYEE_URL, param).then(res => {
      dispatch({
        type: GET_EMPLOYEE,
        payload: res.data
      });
      callback();
    });
  }
}

export function createEmployee(param: CreateRequest, callback: () => void) {
  return (dispatch: Dispatch) => {
    post(CREATE_EMPLOYEE_URL, param).then(res => {
      dispatch({
        type: CREATE_EMPLOYEE,
        payload: {
          name: param.name,
          department: department[param.departmentId],
          departmentId: param.departmentId,
          hiredate: param.hiredate,
          level: level[param.levelId],
          levelId: param.levelId,
          ...res.data
        }
      });
      callback();
    });
  }
}

export function deleteEmployee(param: DeleteRequest) {
  return (dispatch: Dispatch) => {
    post(DELETE_EMPLOYEE_URL, param).then(res => {
      dispatch({
        type: DELETE_EMPLOYEE,
        payload: param.id
      })
    });
  }
}

export function updateEmployee(param: UpdateRequest, callback: () => void) {
  return (dispatch: Dispatch) => {
    post(UPDATE_EMPLOYEE_URL, param).then(res => {
      dispatch({
        type: UPDATE_EMPLOYEE,
        payload: param
      });
      callback();
    });
  }
}

export default function(state = initialState, action: Action) {
  switch (action.type) {
    case GET_EMPLOYEE:
      return {
        ...state,
        employeeList: action.payload
      }
    case CREATE_EMPLOYEE:
      let newList = [action.payload, ...(state.employeeList as EmployeeInfo[])]
      return {
        ...state,
        employeeList: newList
      }
    case DELETE_EMPLOYEE:
      let reducedList = [...(state.employeeList as EmployeeInfo[])];
      _.remove(reducedList, (item: EmployeeInfo) => {
        return item.id === action.payload
      });
      return {
        ...state,
        employeeList: reducedList
      }
    case UPDATE_EMPLOYEE:
      let updatedList = [...(state.employeeList as EmployeeInfo[])];
      let item: UpdateRequest = action.payload;
      let index = _.findIndex(updatedList, {
        id: item.id
      });
      updatedList[index] = {
        id: item.id,
        key: item.id,
        name: item.name,
        department: department[item.departmentId],
        departmentId: item.departmentId,
        hiredate: item.hiredate,
        level: level[item.levelId],
        levelId: item.levelId
      }
      return {
        ...state,
        employeeList: updatedList
      }
    default:
      return state
  }
}

2.3 服务端使用 Typescript

项目地址 https://github.com/poetries/typescript-in-action/tree/master/ts-express

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文