创建一个新的 React 工程,并配置 Redux 和 Ant Design,你可以按以下步骤操作。我将使用 create-react-app
脚手架工具来快速创建一个基于 TypeScript 的 React 项目
1. 创建新项目
使用 create-react-app
创建一个新的 React 项目,带 TypeScript 支持:
npx create-react-app my-project --template typescript
进入到项目目录:
cd my-project
2. 安装依赖
在项目目录下,安装必要的依赖项:
npm install redux react-redux immer antd
npm install @types/react @types/react-dom @types/react-redux --save-dev
3. 项目结构
my-project/
├── src/
│ ├── components/
│ │ └── GiftPanel/
│ │ └── index.tsx
│ ├── redux/
│ │ ├── index.tsx
│ │ ├── init-store.ts
│ ├── constants.ts
│ ├── utils/logger.ts
│ ├── App.tsx
│ └── index.tsx
├── package.json
└── tsconfig.json
如果报错:
情况1:Cannot find module 'ajv/dist/compile/codegen'
删除 node_modules
重新 npm install
安装特定依赖的版本
如果使用特定的库如 react-scripts
,它可能需要特定版本的 ajv
。尝试以下命令:
npm install ajv@^8.0.0
检查 package.json
确保 package.json
中的依赖项没有版本冲突:
{"name": "my-project","version": "0.1.0","private": true,"dependencies": {"@testing-library/jest-dom": "^5.17.0","@testing-library/react": "^13.4.0","@testing-library/user-event": "^13.5.0","@types/jest": "^27.5.2","@types/node": "^16.18.19","ajv": "^8.17.1","antd": "^5.2.2","immer": "^10.1.1","react": "^18.2.0","react-dom": "^18.2.0","react-redux": "^8.0.5","react-scripts": "5.0.1","redux": "^4.2.1","typescript": "^4.9.5","web-vitals": "^2.1.4"},"scripts": {"start": "react-scripts start","build": "react-scripts build","test": "react-scripts test","eject": "react-scripts eject"},"eslintConfig": {"extends": ["react-app","react-app/jest"]},"browserslist": {"production": [">0.2%","not dead","not op_mini all"],"development": ["last 1 chrome version","last 1 firefox version","last 1 safari version"]},"devDependencies": {"@types/react": "^18.0.26","@types/react-dom": "^18.0.10","@types/react-redux": "^7.1.23"}
}
情况2:
可能问题
produce
函数的参数类型问题。- TypeScript 类型声明问题。
错误修复
可以尝试以下步骤来修复你的代码:
- 检查
immer
的版本:确保immer
库已正确安装,并且版本与项目需求兼容。
检查 TypeScript 版本
确保项目使用的 TypeScript 版本兼容项目中的依赖:
npm install typescript@latest --save-dev
import { createStore, combineReducers, AnyAction } from 'redux';
import produce, { enableAllPlugins, Draft } from 'immer';
import { useSelector as useReactReduxSelector } from 'react-redux';
import { initStore, StoreState } from './init-store';
import logger from '../utils/logger';enableAllPlugins();function reducer(state: StoreState = initStore, action: AnyAction): StoreState {const { type, payload } = action;switch (type) {case 'PRODUCE':const { cb } = payload;return produce(state, (draft: Draft<StoreState>) => cb(draft));default:return state;}
}const reducers = {updateReducer: reducer,
};const baseAction = (cb: (draftState: Draft<StoreState>) => void) => ({type: 'PRODUCE',payload: { cb }
});export const actions = { baseAction };export function useSelector<U>(selector: (state: StoreState) => U,equalityFn?: ((left: any, right: any) => boolean) | undefined
) {return useReactReduxSelector<{ updateReducer: StoreState }, U>((state) => selector(state.updateReducer),equalityFn);
}const store = createStore(combineReducers(reducers));store.subscribe(() => {logger.log('store update========', JSON.stringify(store.getState()));
});export default store;
4. 创建和调整文件
-
src/redux/init-store.ts
import { AppState } from '../constants';// 定义 Order 和 User 接口
interface Order {orderId: number;userName: string;productName: string;price: number;status: '已支付' | '未支付';
}interface User {userId: number;userName: string;
}// 定义 StoreState 接口
export interface StoreState {testState: number;appState: AppState;hasGetGiftList: boolean;users: User[];orders: Order[];
}export const initStore: StoreState = {testState: 0,appState: AppState.Init,hasGetGiftList: false,users: [],orders: [{ orderId: 1, userName: 'Alice', productName: 'Product A', price: 100, status: '已支付' },{ orderId: 2, userName: 'Bob', productName: 'Product B', price: 150, status: '未支付' },{ orderId: 3, userName: 'Carol', productName: 'Product C', price: 200, status: '已支付' },{ orderId: 4, userName: 'David', productName: 'Product D', price: 250, status: '未支付' },{ orderId: 5, userName: 'Eve', productName: 'Product E', price: 300, status: '已支付' },{ orderId: 6, userName: 'Frank', productName: 'Product F', price: 350, status: '未支付' },{ orderId: 7, userName: 'Grace', productName: 'Product G', price: 400, status: '已支付' },{ orderId: 8, userName: 'Heidi', productName: 'Product H', price: 450, status: '未支付' },{ orderId: 9, userName: 'Ivan', productName: 'Product I', price: 500, status: '已支付' },{ orderId: 10, userName: 'Judy', productName: 'Product J', price: 550, status: '未支付' },{ orderId: 11, userName: 'Mallory', productName: 'Product K', price: 600, status: '未支付' },{ orderId: 12, userName: 'Oscar', productName: 'Product L', price: 650, status: '已支付' },{ orderId: 13, userName: 'Peggy', productName: 'Product M', price: 700, status: '已支付' },] // 初始化的订单数据
};
-
src/redux/index.tsx
import { createStore, combineReducers, AnyAction } from 'redux';
import { produce } from 'immer';
import { useSelector as useReactReduxSelector } from 'react-redux';
import { initStore, StoreState } from './init-store';
import logger from '../utils/logger';function reducer(state: StoreState = initStore, action: AnyAction): StoreState {const { type, payload } = action;switch (type) {case 'PRODUCE':const { cb } = payload;return produce(state, cb);default:return state;}
}const reducers = {updateReducer: reducer,
};const baseAction = (cb: (draftState: StoreState) => void) => ({type: 'PRODUCE',payload: { cb }
});export const actions = { baseAction };export function useSelector<U>(selector: (state: StoreState) => U,equalityFn?: ((left: any, right: any) => boolean) | undefined
) {return useReactReduxSelector<{ updateReducer: StoreState }, U>((state) => selector(state.updateReducer),equalityFn);
}const store = createStore(combineReducers(reducers));store.subscribe(() => {logger.log('store update========', JSON.stringify(store.getState()));
});export default store;
-
src/constants.ts
export enum AppState {Init,View,Setting
}
-
src/utils/logger.ts
const logger = {log: console.log
};export default logger;
-
src/components/GiftPanel/index.tsx
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { Table, Button, Modal, Form, Input, InputNumber, Select, Popconfirm, message, Pagination, ConfigProvider } from 'antd';
import zhCN from 'antd/lib/locale/zh_CN'; // 引入中文语言包
import { actions, useSelector } from '../../redux';interface Order {orderId: number;userName: string;productName: string;price: number;status: '已支付' | '未支付';
}const { Option } = Select;const GiftPanel = () => {const dispatch = useDispatch();const orders = useSelector((state) => state.orders);const [isAddModalVisible, setIsAddModalVisible] = useState(false);const [isViewModalVisible, setIsViewModalVisible] = useState(false);const [form] = Form.useForm();const [currentOrder, setCurrentOrder] = useState<Order | null>(null);const [currentPage, setCurrentPage] = useState(1);const [pageSize, setPageSize] = useState(10);const showAddModal = () => {setIsAddModalVisible(true);};const handleAddOk = () => {form.validateFields().then(values => {dispatch(actions.baseAction(draftState => {draftState.orders.push({ ...values, orderId: Date.now() });}));setIsAddModalVisible(false);form.resetFields();}).catch(info => {console.log('Validate Failed:', info);});};const handleAddCancel = () => {setIsAddModalVisible(false);form.resetFields();};const showViewModal = (order: Order) => {setCurrentOrder(order);setIsViewModalVisible(true);};const handleViewCancel = () => {setIsViewModalVisible(false);setCurrentOrder(null);};const handleDelete = (orderId: number) => {dispatch(actions.baseAction(draftState => {draftState.orders = draftState.orders.filter(order => order.orderId !== orderId);}));message.success('删除成功');};const handlePageChange = (page: number, pageSize?: number) => {setCurrentPage(page);if (pageSize) {setPageSize(pageSize);}};const columns = [{title: '订单ID',dataIndex: 'orderId',key: 'orderId',},{title: '用户名',dataIndex: 'userName',key: 'userName',},{title: '商品名称',dataIndex: 'productName',key: 'productName',},{title: '价格',dataIndex: 'price',key: 'price',},{title: '支付状态',dataIndex: 'status',key: 'status',},{title: '操作',key: 'action',render: (text: any, record: Order) => (<div><Button onClick={() => showViewModal(record)} style={{ marginRight: 8 }}>查看</Button><Popconfirmtitle="确认删除吗?"onConfirm={() => handleDelete(record.orderId)}okText="确认"cancelText="取消"><Button danger>删除</Button></Popconfirm></div>),},];// 计算当前页的数据const startIndex = (currentPage - 1) * pageSize;const endIndex = startIndex + pageSize;const currentData = orders.slice(startIndex, endIndex);return (<ConfigProvider locale={zhCN}><div><Button type="primary" onClick={showAddModal} style={{ marginBottom: '10px' }}>增加订单</Button><Tablecolumns={columns}dataSource={currentData}rowKey="orderId"pagination={false}/><div style={{ textAlign: 'right', marginTop: 20,display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}><Paginationtotal={orders.length}current={currentPage}pageSize={pageSize}onChange={handlePageChange}showTotal={total => `共 ${total} 条`}showSizeChangershowQuickJumper/></div><Modaltitle="添加订单"visible={isAddModalVisible}onOk={handleAddOk}onCancel={handleAddCancel}okText="添加"cancelText="取消"><Form form={form} layout="vertical" name="form_in_modal"><Form.Itemname="userName"label="用户名"rules={[{ required: true, message: '请输入用户名!' }]}><Input /></Form.Item><Form.Itemname="productName"label="商品名称"rules={[{ required: true, message: '请输入商品名称!' }]}><Input /></Form.Item><Form.Itemname="price"label="价格"rules={[{ required: true, message: '请输入价格!' }]}><InputNumber style={{ width: '100%' }} /></Form.Item><Form.Itemname="status"label="支付状态"rules={[{ required: true, message: '请选择支付状态!' }]}><Select><Option value="已支付">已支付</Option><Option value="未支付">未支付</Option></Select></Form.Item></Form></Modal><Modaltitle="查看订单"visible={isViewModalVisible}onCancel={handleViewCancel}footer={null}>{currentOrder && (<div><p>订单ID: {currentOrder.orderId}</p><p>用户名: {currentOrder.userName}</p><p>商品名称: {currentOrder.productName}</p><p>价格: {currentOrder.price}</p><p>支付状态: {currentOrder.status}</p></div>)}</Modal></div></ConfigProvider>);
};export default GiftPanel;
-
src/App.tsx
import React from 'react';
import GiftPanel from './components/GiftPanel';const App = () => (<div><GiftPanel /></div>
);export default App;
-
src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './redux';
import App from './App';ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById('root')
);
5. 运行项目
在项目目录下运行:
npm start
这将启动开发服务器,打开浏览器访问 http://localhost:3000
以查看你的应用。
通过这个过程,你创建了一个带有 Redux 状态管理和 Ant Design 组件的 React 项目。你可以根据这个基础继续扩展和个性化你的应用
详细解释这个项目的工作原理。这个项目主要是一个使用 React 和 Redux 进行状态管理的订单管理页面,并且使用了 Ant Design 组件库来构建用户界面。以下是详细的解释:
1. 文件结构
项目的主要文件包括:
init-store.ts
: 这个文件初始化了 Redux 的 store 以及一些假数据。GiftPanel/index.tsx
: 这个文件包含了主组件,用于展示订单列表,并包含分页、添加订单和删除订单的功能。
2. init-store.ts
这个文件定义了应用的初始状态并提供了一些假数据。
主要内容:
- 接口定义:定义了
Order
和StoreState
接口。Order
接口描述了订单对象的结构,而StoreState
是应用状态的结构。 - 初始状态:定义了包含初始化订单数组的
initStore
,这些数据在应用加载时展示在表格中
import { AppState } from '../constants';interface Order {orderId: number;userName: string;productName: string;price: number;status: '已支付' | '未支付';
}export interface StoreState {testState: number;appState: AppState;hasGetGiftList: boolean;users: User[];orders: Order[];
}export const initStore: StoreState = {testState: 0,appState: AppState.Init,hasGetGiftList: false,users: [],orders: [/* 初始订单数据 */]
};
3. GiftPanel/index.tsx
这个文件是应用的主要组件,包含表格展示和操作的逻辑。
状态管理:
- 使用
useState
来管理本地状态,如模态框的可见性、当前页码和页大小。 - 使用
useSelector
从 Redux store 中获取订单数据。 - 使用
useDispatch
进行状态更新。
主要功能:
-
显示订单表格:
- 使用 Ant Design 的
Table
组件展示订单数据。表格数据来源于 Redux store。 - 表头(columns)定义了订单的字段和操作按钮。
- 使用 Ant Design 的
-
分页:
- 使用 Ant Design 的
Pagination
组件来实现分页功能。 - 通过计算
currentPage
和pageSize
,确定当前页面显示的数据片段。
- 使用 Ant Design 的
-
添加订单:
- 使用 Ant Design 的
Modal
和Form
组件,通过表单输入新订单数据。 - 表单验证通过后,触发
handleAddOk
更新 Redux store。
- 使用 Ant Design 的
-
删除订单:
- 使用
Popconfirm
组件确认删除操作。 - 确认删除后,通过
handleDelete
更新 Redux store。
- 使用
-
查看订单:
- 点击查看按钮,展示订单详细信息。
// 初始化状态
const [isAddModalVisible, setIsAddModalVisible] = useState(false);
const [isViewModalVisible, setIsViewModalVisible] = useState(false);
const [form] = Form.useForm();
const [currentOrder, setCurrentOrder] = useState<Order | null>(null);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);// 页码变化处理
const handlePageChange = (page: number, pageSize?: number) => {setCurrentPage(page);if (pageSize) {setPageSize(pageSize);}
};// 计算当前页的数据
const startIndex = (currentPage - 1) * pageSize;
const endIndex = startIndex + pageSize;
const currentData = orders.slice(startIndex, endIndex);return (<ConfigProvider locale={zhCN}><div><Button type="primary" onClick={() => setIsAddModalVisible(true)} style={{ marginBottom: '10px' }}>增加订单</Button><Table columns={columns} dataSource={currentData} rowKey="orderId"pagination={false}/><div style={{ textAlign: 'right', marginTop: 20 }}><Paginationtotal={orders.length}current={currentPage}pageSize={pageSize}onChange={handlePageChange}showTotal={total => `共 ${total} 条`}showSizeChangershowQuickJumper/></div></div></ConfigProvider>
);
解释:
- 语言设置:使用
ConfigProvider
包裹组件并设置语言为中文。 - 状态管理:使用
useState
管理组件本地状态,如模态框的可见性和分页信息,使用useSelector
从 Redux store 获取订单数据,使用useDispatch
更新 Redux store。 - 订单表格展示:定义表格列结构,使用 Ant Design 的
Table
组件展示订单数据。 - 分页:使用
Pagination
组件实现分页,通过handlePageChange
处理分页变化。 - 订单操作:实现添加、查看和删除订单的操作。
这个项目的核心在于利用 React 和 Redux 进行状态管理,并结合 Ant Design 组件库构建用户界面。