文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
二、React 项目实践
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
中的 jsx
为 react
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论