ReactJS 教程

ReactJS - 主页 ReactJS - 简介 ReactJS - 路线图 ReactJS - 安装 ReactJS - 功能 ReactJS - 优势和缺点 ReactJS - 架构 ReactJS - 创建 React 应用程序 ReactJS - JSX ReactJS - 组件 ReactJS - 嵌套组件 ReactJS - 使用组件 ReactJS - 集合组件 ReactJS - 样式 ReactJS - 属性 (props) ReactJS - 使用属性创建组件 ReactJS - props 验证 ReactJS - 构造函数 ReactJS - 组件生命周期 ReactJS - 事件管理 ReactJS - 创建事件感知组件 ReactJS - Expense Manager 事件 ReactJS - 状态管理 ReactJS - 状态管理 API ReactJS - 无状态组件 ReactJS - Hooks 进行状态管理 ReactJS - Hooks 的组件生命周期 ReactJS - 布局组件 ReactJS - 分页 ReactJS - Material UI ReactJS - Http 客户端编程 ReactJS - 表单编程 ReactJS - 受控组件 ReactJS - 非受控组件 ReactJS - Formik ReactJS - 条件渲染 ReactJS - 列表 ReactJS - Key 键 ReactJS - 路由 ReactJS - Redux ReactJS - 动画 ReactJS - Bootstrap ReactJS - Map ReactJS - 表格 ReactJS - 使用 Flux 管理状态 ReactJS - 测试 ReactJS - CLI 命令 ReactJS - 构建和部署 ReactJS - 示例

Hooks

ReactJS - Hooks 简介 ReactJS - 使用 useState ReactJS - 使用 useEffect ReactJS - 使用 useContext ReactJS - 使用 useRef ReactJS - 使用 useReducer ReactJS - 使用 useCallback ReactJS - 使用 useMemo ReactJS - 自定义 Hooks

ReactJS 高级

ReactJS - 可访问性 ReactJS - 代码拆分 ReactJS - 上下文 ReactJS - 错误边界 ReactJS - 转发 Refs ReactJS - 片段 ReactJS - 高阶组件 ReactJS - 与其他库集成 ReactJS - 优化性能 ReactJS - Profiler API ReactJS - Portals ReactJS - 不使用 ES6 ECMAScript ReactJS - 不使用 JSX 的 React ReactJS - Reconciliation ReactJS - Refs 和 DOM ReactJS - 渲染道具 ReactJS - 静态类型检查 ReactJS - 严格模式 ReactJS - Web 组件

其他概念

ReactJS - 日期选择器 ReactJS - Helmet ReactJS - 内联样式 ReactJS - PropTypes ReactJS - BrowserRouter ReactJS - DOM ReactJS - 轮播 ReactJS - 图标 ReactJS - 表单组件 ReactJS - 参考 API

ReactJS 有用资源

ReactJS - 快速指南 ReactJS - 备忘录 Axios - 备忘录 ReactJS - 有用资源 ReactJS - 讨论


ReactJS - 状态管理

我们将执行以下操作来管理我们的 redux 存储。

  • 通过异步获取 api 从服务器获取费用并将其设置在 Redux 存储中。

  • 通过异步获取编程将新费用添加到服务器,并在 Redux 存储中添加新费用。

  • 通过异步获取 api 从服务器删除现有费用并更新 Redux 存储。

让我们创建操作类型、操作创建器、操作和 Reducer 来管理 Redux 状态。

在 src 文件夹下创建一个文件夹 actions

接下来,创建一个文件 types.js 来创建操作类型。

export const LIST_EXPENSE_STARTED = 'LIST_EXPENSE_STARTED';
export const LIST_EXPENSE_SUCCESS = 'LIST_EXPENSE_SUCCESS';
export const LIST_EXPENSE_FAILURE = 'LIST_EXPENSE_FAILURE';

export const ADD_EXPENSE_STARTED = 'ADD_EXPENSE_STARTED';
export const ADD_EXPENSE_SUCCESS = 'ADD_EXPENSE_SUCCESS';
export const ADD_EXPENSE_FAILURE = 'ADD_EXPENSE_FAILURE';

export const DELETE_EXPENSE_STARTED = 'DELETE_EXPENSE_STARTED';
export const DELETE_EXPENSE_SUCCESS = 'DELETE_EXPENSE_SUCCESS';
export const DELETE_EXPENSE_FAILURE = 'DELETE_EXPENSE_FAILURE';

接下来,在actions文件夹下创建一个文件index.js来创建动作创建器。

import {
   LIST_EXPENSE_STARTED, LIST_EXPENSE_SUCCESS, LIST_EXPENSE_FAILURE,
   ADD_EXPENSE_STARTED, ADD_EXPENSE_SUCCESS, ADD_EXPENSE_FAILURE,
   DELETE_EXPENSE_STARTED, DELETE_EXPENSE_SUCCESS, DELETE_EXPENSE_FAILURE,
} from "./types";
export const getExpenseListStarted = () => {
   return {
      type: LIST_EXPENSE_STARTED
   }
}
export const getExpenseListSuccess = data => {
   return {
      type: LIST_EXPENSE_SUCCESS,
      payload: {
         data
      }
   }
}
export const getExpenseListFailure = error => {
   return {
      type: LIST_EXPENSE_FAILURE,
      payload: {
         error
      }
   }
}
export const addExpenseStarted = () => {
   return {
      type: ADD_EXPENSE_STARTED
   }
}
export const addExpenseSuccess = data => {
   return {
      type: ADD_EXPENSE_SUCCESS,
      payload: {
         data
      }
   }
}
export const addExpenseFailure = error => {
   return {
      type: ADD_EXPENSE_FAILURE,
      payload: {
         error
      }
   }
}
export const deleteExpenseStarted = () => {
   return {
      type: DELETE_EXPENSE_STARTED
   }
}
export const deleteExpenseSuccess = data => {
   return {
      type: DELETE_EXPENSE_SUCCESS,
      payload: {
         data
      }
   }
}
export const deleteExpenseFailure = error => {
   return {
      type: DELETE_EXPENSE_FAILURE,
      payload: {
         error
      }
   }
}

在这里,我们为 fetch api 的每种可能结果(成功、失败和错误)创建了一个动作创建器。由于我们将使用三个 web api 调用,并且每个调用将有三种可能的结果,因此我们使用了 9 个动作创建器。

接下来,在actions文件夹下创建一个文件expenseActions.js,并创建三个函数来获取、添加和删除费用并分派状态更改。

import {
   getExpenseListStarted, getExpenseListSuccess, getExpenseListFailure,
   addExpenseStarted, addExpenseSuccess, addExpenseFailure,
   deleteExpenseStarted, deleteExpenseSuccess, deleteExpenseFailure
} from "./index";
export const getExpenseList = () => async dispatch => {
   dispatch(getExpenseListStarted());
   try {
      const res = await fetch('http://localhost:8000/api/expenses');
      const data = await res.json();
      var items = [];
      data.forEach((item) => {
         let newItem = {
            id: item._id,
            name: item.name,
            amount: item.amount,
            spendDate: item.spend_date,
            category: item.category
         }
         items.push(newItem)
      });
      dispatch(getExpenseListSuccess(items));
   } catch (err) {
      dispatch(getExpenseListFailure(err.message));
   }
}
export const addExpense = (data) => async dispatch => {
   dispatch(addExpenseStarted());

   let newItem = {
      name: data.name,
      amount: data.amount,
      spend_date: data.spendDate,
      category: data.category
   }
   console.log(newItem);
   try {
      const res = await fetch('http://localhost:8000/api/expense', {
         method: 'POST',
         body: JSON.stringify(newItem),
         headers: {
            "Content-type": "application/json; charset=UTF-8"
         } 
      });
      const data = await res.json();
      newItem.id = data._id;
      dispatch(addExpenseSuccess(newItem));
   } catch (err) {
      console.log(err);
      dispatch(addExpenseFailure(err.message));
   }
}
export const deleteExpense = (id) => async dispatch => {
   dispatch(deleteExpenseStarted());
   try {
      const res = await fetch('http://localhost:8000/api/expense/' + id, {
         method: 'DELETE'
      });
      const data = await res.json();
      dispatch(deleteExpenseSuccess(id));
   } catch (err) {
      dispatch(deleteExpenseFailure(err.message));
   }
}

这里,

  • 使用异步获取 API 进行 Web API 调用。

  • 使用调度函数在成功、失败和错误事件期间调度适当的操作。

src 文件夹下创建一个文件夹 reducers,并在 reducers 文件夹下创建一个文件 index.js 来创建 Redux Reducers。

import {
   LIST_EXPENSE_STARTED, LIST_EXPENSE_SUCCESS, LIST_EXPENSE_FAILURE,
   ADD_EXPENSE_STARTED, ADD_EXPENSE_SUCCESS, ADD_EXPENSE_FAILURE,
   DELETE_EXPENSE_STARTED, DELETE_EXPENSE_SUCCESS, DELETE_EXPENSE_FAILURE
} from "../actions/types";

// 定义用户的初始状态
const initialState = {
   data: null,
   loading: false,
   error: null
}
export default function expenseReducer(state = initialState, action) {
   switch (action.type) {
      case LIST_EXPENSE_STARTED:
         return {
            ...state,
            loading: true
         }
      case LIST_EXPENSE_SUCCESS:
         const { data } = action.payload;
         return {
            ...state,
            data,
            loading: false
         }
      case LIST_EXPENSE_FAILURE:
         const { error } = action.payload;
         return {
            ...state,
            error
         }
      case ADD_EXPENSE_STARTED:
         return {
            ...state,
            loading: true
         }
      case ADD_EXPENSE_SUCCESS:
         return {
            ...state,
            loading: false
         }
      case ADD_EXPENSE_FAILURE:
         const { expenseError } = action.payload;
         return {
            ...state,
            expenseError
         }
      case DELETE_EXPENSE_STARTED:
         return {
            ...state,
            loading: true
         }
      case DELETE_EXPENSE_SUCCESS:
         return {
            ...state,
            data: state.data.filter(expense => expense.id !== action.payload.data),
            loading: false
         }
      case DELETE_EXPENSE_FAILURE:
         const { deleteError } = action.payload;
         return {
            ...state,
            deleteError
         }
      default:
         return state
   }
}

在这里,我们更新了每种操作类型的 redux 存储状态。

接下来,打开 src 文件夹下的 index.js 文件并包含 Provider 组件,以便所有组件都可以连接并使用 redux 存储。

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import rootReducer from './reducers';
import App from './components/App';

const store = createStore(rootReducer, applyMiddleware(thunk));

ReactDOM.render(
   <Provider store={store}>
      <App />
   </Provider>,
   document.getElementById('root')
);

这里,

  • 导入 createStore 和 applyMiddleware
  • 从 redux-thunk 库导入 thunk(用于异步获取 api)
  • 从 redux 库导入 Provider
  • 通过配置 Reducer 和 thunk 中间件使用 createStore 创建新存储
  • 将 Provider 组件作为顶级组件与 redux store 连接起来

reactjs_example.html