创建文件

这是一个经典的Redux模版,如果你对redux比较陌生,可以放弃使用 redux ,选择适合自己的代码习惯

此示例为基于Redux的高阶组件,如简单todolist可不遵循下方示例

  1. 在SRC文件夹中,创建一个新文件夹,这里示例TODOLIST
  2. todolist下创建如下对应目录
  1. todoPage.tsx //入口页面,内含todolist和子组件todoControl
  2. todoControl.tsx //操控组件
  3. todoList.tsx //列表控件
  4. └─service //每个业务文件夹下必含该文件夹及其下属文件
  5. todoAction.ts //主要是功能处理
  6. todoReducers.ts //判断处理
  7. todoTypes.ts //内含有枚举,辅助reducer

书写代码

主要功能:从网络中获取现有todolist,并且赋予其增删功能(增删暂为本地)

确定好功能后,大概有这几种操作

  1. export enum ETodoTypes {
  2. TODO_FETCH_LOADING = "TODO_FETCH_LOADING", //开始网络获取
  3. TODO_FETCH_SUCCESS = "TODO_FETCH_SUCCESS", //网络获取成功
  4. TODO_FETCH_ERROR = "TODO_FETCH_ERROR", //网络获取失败
  5. TODO_FETCH_REFUSH ="TODO_FETCH_REFUSH",
  6. TODO_ADD_ITEM = 'TODO_ADD_ITEM', //本地增加一条
  7. TODO_REMOVE_ITEM ='TODO_REMOVE_ITEM', //本地移除一条
  8. }

将他们书写在todoTypes.ts中,并将该文件引入 src/store/types/index.ts中,方便使用

  1. export * from "../../todolist/service/todoTypes";

todoReducers.ts书写

引入刚刚写好的ETodoTypesstore目录下IInitialState

  1. import { ETodoTypes, IInitialState } from "../../store/types";
  2. interface ITodo {
  3. userId: number,
  4. id: number,
  5. title: string,
  6. completed: boolean
  7. }
  8. interface INITSTATE {
  9. isLoading: boolean,
  10. todolist: Array<ITodo>
  11. }
  12. const initialState: INITSTATE = {
  13. isLoading: true,
  14. todolist: []
  15. };
  16. export const todoReducer = (state = initialState, action: IInitialState) => {
  17. switch (action.type) {
  18. case ETodoTypes.TODO_FETCH_LOADING:
  19. return {
  20. ...state,
  21. isLoading: true
  22. };
  23. case ETodoTypes.TODO_FETCH_SUCCESS:
  24. return {
  25. ...state,
  26. isLoading: false,
  27. todolist: action.payload as Array<ITodo>
  28. };
  29. case ETodoTypes.TODO_FETCH_REFUSH:
  30. return {
  31. ...state,
  32. isLoading: false,
  33. todolist: action.payload as Array<ITodo>
  34. };
  35. case ETodoTypes.TODO_FETCH_ERROR:
  36. return {
  37. ...state,
  38. isLoading: false,
  39. todolist: action.payload as Array<ITodo>
  40. };
  41. case ETodoTypes.TODO_ADD_ITEM:
  42. return {
  43. ...state,
  44. isLoading: false,
  45. todolist: action.payload as Array<ITodo>
  46. };
  47. case ETodoTypes.TODO_REMOVE_ITEM:
  48. return {
  49. ...state,
  50. isLoading: false,
  51. todolist: action.payload as Array<ITodo>
  52. };
  53. default:
  54. return state;
  55. }
  56. };

将该文件引入 src/store/reducers/index.ts

  1. export * from "../../todolist/service/todoReducers";

将写好的reducer引入/store/index.ts

  1. import { createStore, combineReducers, applyMiddleware } from "redux";
  2. import thunkMiddleware from "redux-thunk";
  3. import { composeWithDevTools } from "redux-devtools-extension";
  4. import { userReducer,todoReducer } from "./reducers";
  5. // import { chatReducer } from "./chat/reducers";
  6. const rootReducer = combineReducers({
  7. user: userReducer,
  8. todo:todoReducer
  9. });
  10. export type AppState = ReturnType<typeof userReducer>;
  11. export const configureStore = () => {
  12. const middlewares = [thunkMiddleware];
  13. const middleWareEnhancer = applyMiddleware(...middlewares);
  14. const store = createStore(
  15. rootReducer,
  16. composeWithDevTools(middleWareEnhancer)
  17. );
  18. return store;
  19. };

到这里,恭喜你,你已经完成了一大步,剩下的,就可以开心写代码了

接下来,我们在todoAction中为todoList书写三个功能

  1. import { Dispatch } from "redux";
  2. import axios from "../../utils/http";
  3. import { ETodoTypes } from "../../store/types";
  4. import { IInitialState } from "../../store/types";
  5. export const getTodoList = () => async (dispatch: Dispatch<IInitialState>) => {
  6. dispatch({ type: ETodoTypes.TODO_FETCH_LOADING });
  7. try {
  8. const res = await axios.get(`http://jsonplaceholder.typicode.com/todos`);
  9. dispatch({ type: ETodoTypes.TODO_FETCH_SUCCESS, payload: res.data });
  10. } catch (error) {
  11. dispatch({ type: ETodoTypes.TODO_FETCH_ERROR });
  12. }
  13. };

todolist.tsx书写

我们知道它是由一个列表组成,那么我们引入antd中的列表组件,为他生成一个list

  1. import React from "react";
  2. import { connect } from "react-redux";
  3. import { List, Button } from 'antd';
  4. import { getTodoList } from "./service/todoAction";
  5. import { AppState } from "../store";
  6. interface ITodoProps {
  7. state: AppState;
  8. getTodoList: () => void;
  9. }
  10. class TodoList extends React.Component<ITodoProps> {
  11. render() {
  12. console.log(this.props);
  13. const { state } = this.props;
  14. return <div>
  15. <List
  16. size="small"
  17. bordered
  18. loading={state.todo.isLoading}
  19. dataSource={state.todo.todolist}
  20. renderItem={(item) => <List.Item>{item.title}<Button>删除</Button></List.Item>}
  21. />
  22. </div>;
  23. }
  24. componentDidMount() {
  25. this.props.getTodoList()
  26. }
  27. }
  28. const mapStateToProps = (state: AppState) => ({
  29. state
  30. });
  31. const mapDispatchToProps = (dispatch: any) => ({
  32. getTodoList: () => dispatch(getTodoList())
  33. });
  34. export default connect(mapStateToProps, mapDispatchToProps)(TodoList);

看,他已经可以顺利加载数据了

接下来我们在每一条后面,增加一个删除按钮
todoAction.ts中增加如下

  1. export const removeItem = (todos: Array<any>, index: number) => (dispatch: Dispatch<IInitialState>) => {
  2. dispatch({ type: ETodoTypes.TODO_FETCH_LOADING });
  3. try {
  4. todos.splice(index, 1)
  5. dispatch({ type: ETodoTypes.TODO_REMOVE_ITEM, payload: todos });
  6. } catch (error) {
  7. dispatch({ type: ETodoTypes.TODO_FETCH_ERROR });
  8. }
  9. };

todoListmapDispatchToProps中也增加相应的

  1. removeItem: (todos: Array<any>, index: number) => dispatch(removeItem(todos, index))

在按钮中调用该方法即可

todoControl的书写

todoControl主要是有两个功能,一个是增加一条,一个是刷新列表

他独立于todoList,属于两个子组件通信,其实我写到这里,我也没有尝试过
让我们来一起试一下

todoControl中代码如下

  1. import React, { Component } from 'react'
  2. import { connect } from 'react-redux';
  3. import { AppState } from '../store';
  4. import { addItem, refreshTodo } from './service/todoAction';
  5. import { Button } from 'antd';
  6. interface ITodoControlProps {
  7. state: AppState;
  8. refreshTodo: () => void;
  9. addItem: (todos: Array<any>) => void;
  10. }
  11. class TodoControl extends Component<ITodoControlProps>{
  12. render() {
  13. const { addItem, state, refreshTodo } = this.props
  14. return (
  15. <div>
  16. <Button onClick={refreshTodo}>刷新</Button>
  17. <Button onClick={() => addItem(state.todo.todolist)}>随机添加</Button>
  18. </div>
  19. )
  20. }
  21. }
  22. const mapStateToProps = (state: AppState) => ({
  23. state
  24. });
  25. const mapDispatchToProps = (dispatch: any) => ({
  26. addItem: (todos: Array<any>) => dispatch(addItem(todos)),
  27. refreshTodo: () => dispatch(refreshTodo()),
  28. });
  29. export default connect(mapStateToProps, mapDispatchToProps)(TodoControl);

todoAction

  1. export const addItem = (todos: Array<any>) => (dispatch: Dispatch<IInitialState>) => {
  2. dispatch({ type: ETodoTypes.TODO_FETCH_LOADING });
  3. try {
  4. let item = {
  5. userId: Math.random(),
  6. id: Math.random(),
  7. title: `Title:${Math.random()}`,
  8. completed: false
  9. }
  10. todos.unshift(item)
  11. dispatch({ type: ETodoTypes.TODO_ADD_ITEM, payload: todos });
  12. } catch (error) {
  13. dispatch({ type: ETodoTypes.TODO_FETCH_ERROR });
  14. }
  15. };
  16. export const refreshTodo = () => async (dispatch: Dispatch<IInitialState>) => {
  17. dispatch({ type: ETodoTypes.TODO_FETCH_LOADING });
  18. try {
  19. const res = await axios.get(`http://jsonplaceholder.typicode.com/todos`);
  20. dispatch({ type: ETodoTypes.TODO_FETCH_SUCCESS, payload: res.data });
  21. } catch (error) {
  22. dispatch({ type: ETodoTypes.TODO_FETCH_ERROR });
  23. }
  24. };

Great! 完成!