📜  React Redux 组件 getById crud 示例 - Javascript (1)

📅  最后修改于: 2023-12-03 14:46:57.860000             🧑  作者: Mango

React Redux 组件 getById CRUD 示例 - Javascript

本示例将展示如何使用React和Redux构建一个获取个人信息的web应用,包含基本的增删改查操作。我们将通过一个示例代码,阐述应用如何拥有以下功能:

  • 获取特定用户信息
  • 添加新用户
  • 编辑用户信息
  • 删除用户信息
技术栈
  • React 16.x
  • Redux
  • React-Redux
  • React-Router-Dom
  • Redux-Thunk
  • Axios
步骤

在开始构建应用之前,请确保您已经安装了Node.js(最好使用最新版本)。 接下来,我们来看看构建步骤:

  1. 安装必须的包
npm i react react-dom react-redux react-router-dom redux redux-thunk axios
  1. 打造基础文件夹结构

在应用的根目录下创建以下目录:

- src
  - components
    - Users.js
    - User.js
  - actions
    - userActions.js
  - reducers
    - userReducer.js
  - utils
    - api.js
  - App.js
  - index.js
  1. 创建用户模型

在这个示例中,我们将使用以下模型:

{
   id: 1,
   name: 'Mark',
   email: 'mark@example.com',
   phone: '1234567890'
}

其中,id是唯一的,name,email和phone是字符串类型。

为了模拟这个模型,我们将使用一个数组来存储用户数据,并将id设置为自增的数字。在src/utils/api.js中创建以下代码:

let users = [
  {
      id: 1,
      name: 'Mark',
      email: 'mark@example.com',
      phone: '1234567890'
  },
  {
     id: 2,
     name: 'John',
     email: 'john@example.com',
     phone: '4567891230'
  }
];

let nextId = 3; //设置初始id为3

export function getUsers() {
   return new Promise((resolve, reject) => {
      setTimeout(() => {
         resolve(users);
      }, 1000);
   });
}

export function getUserById(id) {
   return new Promise((resolve, reject) => {
      setTimeout(() => {
         const user = users.find((user) => user.id === id);
         if (user) {
            resolve(user);
         } else {
            reject(new Error('User not found'))
         }
      }, 500);
   });
}

export function addUser(user) {
   return new Promise((resolve, reject) => {
      setTimeout(() => {
         const newUser = {...user, id: nextId++};
         users.push(newUser);
         resolve(newUser);
      }, 500);
   });
}

export function updateUser(user) {
   return new Promise((resolve, reject) => {
      setTimeout(() => {
         const index = users.findIndex((u) => u.id === user.id);
         if (index === -1) {
            reject(new Error('User not found'));
         } else {
            users[index] = user;
            resolve(user);
         }
      }, 500);
   });
}

export function deleteUser(id) {
   return new Promise((resolve, reject) => {
      setTimeout(() => {
         const index = users.findIndex((u) => u.id === id);
         if (index === -1) {
            reject(new Error('User not found'));
         } else {
            users.splice(index, 1);
            resolve(id);
         }
      }, 500);
   });
}

这段代码模拟了与一个API进行交互的函数,并返回Promise以模拟异步请求。

在上面的代码中,我们定义了五个函数:

  • getUsers:返回所有的users
  • getUserById:从id获取特定用户信息
  • addUser:将一个新用户添加到用户列表中
  • updateUser:根据id更新用户信息
  • deleteUser:通过id删除用户信息
  1. 创建用户操作

src/actions/userActions.js中,创建以下内容:

import {
   FETCH_USERS_REQUEST,
   FETCH_USERS_SUCCESS,
   FETCH_USERS_FAILURE,
   FETCH_USER_REQUEST,
   FETCH_USER_SUCCESS,
   FETCH_USER_FAILURE,
   ADD_USER_REQUEST,
   ADD_USER_SUCCESS,
   ADD_USER_FAILURE,
   UPDATE_USER_REQUEST,
   UPDATE_USER_SUCCESS,
   UPDATE_USER_FAILURE,
   DELETE_USER_REQUEST,
   DELETE_USER_SUCCESS,
   DELETE_USER_FAILURE
} from '../constants/userConstants';

import * as api from '../utils/api';

//获取所有用户
export const fetchUsers = () => async (dispatch) => {
   try {
      dispatch({ type: FETCH_USERS_REQUEST });

      const data = await api.getUsers();

      dispatch({
         type: FETCH_USERS_SUCCESS,
         payload: data,
      })
   } catch (error) {
      dispatch({
         type: FETCH_USERS_FAILURE,
         error: error.message,
      });
   }
};

//根据id获取用户
export const fetchUserById = (id) => async (dispatch) => {
   try {
      dispatch({ type: FETCH_USER_REQUEST });

      const data = await api.getUserById(id);

      dispatch({
         type: FETCH_USER_SUCCESS,
         payload: data,
      });
   } catch (error) {
      dispatch({
         type: FETCH_USER_FAILURE,
         error: error.message,
      });
   }
};

//添加用户
export const addUser = (user) => async (dispatch) => {
   try {
      dispatch({ type: ADD_USER_REQUEST });

      const data = await api.addUser(user);

      dispatch({
         type: ADD_USER_SUCCESS,
         payload: data,
      });
   } catch (error) {
      dispatch({
         type: ADD_USER_FAILURE,
         error: error.message,
      });
   }
};

//更新用户信息
export const updateUser = (user) => async (dispatch) => {
   try {
      dispatch({ type: UPDATE_USER_REQUEST });

      const data = await api.updateUser(user);

      dispatch({
         type: UPDATE_USER_SUCCESS,
         payload: data,
      });
   } catch (error) {
      dispatch({
         type: UPDATE_USER_FAILURE,
         error: error.message,
      });
   }
};

//删除用户信息
export const deleteUser = (id) => async (dispatch) => {
   try {
      dispatch({ type: DELETE_USER_REQUEST });

      const data = await api.deleteUser(id);

      dispatch({
         type: DELETE_USER_SUCCESS,
         payload: data,
      });
   } catch (error) {
      dispatch({
         type: DELETE_USER_FAILURE,
         error: error.message,
      });
   }
};

这里我们定义了五个功能,每一个功能都是一个异步函数。当我们调用其中的一个函数时,它会分派一个action到store,并执行适当的逻辑,最终更新状态并重新渲染与该状态对应的UI。

  1. 创建User Reducer

src/reducers/userReducer.js中,我们将创建一个Reducer来处理我们User的action和状态。

import {
   FETCH_USERS_REQUEST,
   FETCH_USERS_SUCCESS,
   FETCH_USERS_FAILURE,
   FETCH_USER_REQUEST,
   FETCH_USER_SUCCESS,
   FETCH_USER_FAILURE,
   ADD_USER_REQUEST,
   ADD_USER_SUCCESS,
   ADD_USER_FAILURE,
   UPDATE_USER_REQUEST,
   UPDATE_USER_SUCCESS,
   UPDATE_USER_FAILURE,
   DELETE_USER_REQUEST,
   DELETE_USER_SUCCESS,
   DELETE_USER_FAILURE,
} from '../constants/userConstants';

const initialState = {
   users: [],
   user: null,
   loading: false,
   error: null
}

export const userReducer = (state = initialState, action) => {
   switch(action.type) {
      case FETCH_USERS_REQUEST:
         return {
            ...state,
            loading: true,
            error: null,
         };
      case FETCH_USERS_SUCCESS:
         return {
            ...state,
            loading: false,
            error: null,
            users: action.payload,
         };
      case FETCH_USERS_FAILURE:
         return {
            ...state,
            loading: false,
            error: action.error
         };
      case FETCH_USER_REQUEST:
         return {
            ...state,
            loading: true,
            error: null,
         };
      case FETCH_USER_SUCCESS:
         return {
            ...state,
            loading: false,
            error: null,
            user: action.payload,
         };
      case FETCH_USER_FAILURE:
         return {
            ...state,
            loading: false,
            error: action.error
         };
      case ADD_USER_REQUEST:
         return {
            ...state,
            loading: true,
            error: null
         };
      case ADD_USER_SUCCESS:
         return {
            ...state,
            loading: false,
            error: null,
            users: [ ...state.users, action.payload ]
         };
      case ADD_USER_FAILURE:
         return {
            ...state,
            loading: false,
            error: action.error
         };
      case UPDATE_USER_REQUEST:
         return {
            ...state,
            loading: true,
            error: null
         };
      case UPDATE_USER_SUCCESS:
         return {
            ...state,
            loading: false,
            error: null,
            users: state.users.map((user) => {
               if (user.id === action.payload.id) {
                  return { ...user, ...action.payload };
               } else {
                  return user;
               }
            }),
            user: action.payload
         };
      case UPDATE_USER_FAILURE:
         return {
            ...state,
            loading: false,
            error: action.error
         };
      case DELETE_USER_REQUEST:
         return {
            ...state,
            loading: true,
            error: null
         };
      case DELETE_USER_SUCCESS:
         return {
            ...state,
            loading: false,
            error: null,
            users: state.users.filter((user) => user.id !== action.payload)
         };
      case DELETE_USER_FAILURE:
         return {
            ...state,
            loading: false,
            error: action.error
         };
      default:
         return state;
   }
};

这是一个标准的Redux Reducer,用于根据action更新应用程序的状态。当我们调用一个异步操作中的某一个case时,会执行对应的逻辑,最终返回一个新的state。

  1. 创建Users组件

src/components/Users.js中,我们创建一个简单的React组件,用于获取所有用户:

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUsers } from '../actions/userActions';

const Users = () => {
   const dispatch = useDispatch();

   useEffect(() => {
      dispatch(fetchUsers());
   }, [dispatch]);

   const users = useSelector((state) => state.userReducer.users);
   const loading = useSelector((state) => state.userReducer.loading);
   const error = useSelector((state) => state.userReducer.error);

   return (
      <div>
         <h1>Users</h1>
         {loading && <em>Loading users...</em>}
         {error && <p style={{ color: 'red' }}>{error}</p>}
         {!loading && users.length === 0 && <p>No users added yet.</p>}
         {users.length > 0 && (
            <table>
               <thead>
                  <tr>
                     <th>ID</th>
                     <th>Name</th>
                     <th>Email</th>
                     <th>Phone</th>
                  </tr>
               </thead>
               <tbody>
                  {users.map((user) => (
                     <tr key={user.id}>
                        <td>{user.id}</td>
                        <td>{user.name}</td>
                        <td>{user.email}</td>
                        <td>{user.phone}</td>
                     </tr>
                  ))}
               </tbody>
            </table>
         )}
      </div>
   )
}

export default Users;

在这个组件中,我们使用了React Hooks和React-Redux来获取我们的用户数据,并在页面渲染中输出。

  1. 创建User组件

src/components/User.js中,我们可以创建一个用于特定用户的组件。

import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUserById, addUser, updateUser, deleteUser } from '../actions/userActions';

const INITIAL_STATE = { name: '', email: '', phone: '' };

const User = ({ match, history }) => {
   const [user, setUser] = useState(INITIAL_STATE);
   const [isSubmitting, setIsSubmitting] = useState(false);
   const [errors, setErrors] = useState({});
   const { id } = match.params;
   const dispatch = useDispatch();
   const singleUser = useSelector((state) => state.userReducer.user);
   const loading = useSelector((state) => state.userReducer.loading);
   const error = useSelector((state) => state.userReducer.error);

   const handleChange = (e) => setUser({ ...user, [e.target.name]: e.target.value.trim() });

   const validate = () => {
      let newErrors = {};

      if (!user.name) {
         newErrors.name = 'Please provide the name!';
      }

      if (!user.email) {
         newErrors.email = 'Please provide the email!';
      }

      if (!user.phone) {
         newErrors.phone = 'Please provide the phone!';
      }

      return newErrors;
   }

   const handleSubmit = (e) => {
      e.preventDefault();

      setIsSubmitting(true);
      setErrors(validate());

      if (Object.keys(errors).length === 0) {
         if (!id) {
            dispatch(addUser(user)).then(() => history.push('/users'));
         } else {
            dispatch(updateUser(user)).then(() => history.push('/users'));
         }
      } else {
         setIsSubmitting(false);
      }
   };

   const handleDelete = () => {
      if (window.confirm('Are you sure to delete this user?')) {
         dispatch(deleteUser(id)).then(() => history.push('/users'));
      }
   }

   useEffect(() => {
      if (id) {
         dispatch(fetchUserById(id));
      }
   }, [dispatch, id]);

   useEffect(() => {
      if (!isSubmitting) {
         setErrors({});
         setUser(singleUser || INITIAL_STATE);
      }
   }, [singleUser, isSubmitting]);

   return (
      <div>
         {loading && <em>Loading user...</em>}
         {error && <p style={{ color: 'red' }}>{error}</p>}
         {user && (
            <form onSubmit={handleSubmit}>
               <div>
                  <label htmlFor="name">Name:</label>
                  <input type="text" id="name" name="name" value={user.name} onChange={handleChange} />
                  {errors.name && <p style={{ color: 'red' }}>{errors.name}</p>}
               </div>
               <div>
                  <label htmlFor="email">Email:</label>
                  <input type="email" id="email" name="email" value={user.email} onChange={handleChange} />
                  {errors.email && <p style={{ color: 'red' }}>{errors.email}</p>}
               </div>
               <div>
                  <label htmlFor="phone">Phone:</label>
                  <input type="text" id="phone" name="phone" value={user.phone} onChange={handleChange} />
                  {errors.phone && <p style={{ color: 'red' }}>{errors.phone}</p>}
               </div>
               <div>
                  <button type="submit" disabled={isSubmitting}>Save</button>
                  {id && <button type="button" onClick={handleDelete}>Delete</button>}
                  <button type="button" onClick={() => history.push('/users')}>Cancel</button>
               </div>
            </form>
         )}
      </div>
   );
};

export default User;

在这里,我们创建了一个React Hook函数组件,用于处理单个用户的渲染和逻辑。我们可以简单地调用该组件来显示特定的用户信息并进行简单编辑。

  1. 创建Main App Component

最后,在src/App.js中,我们定义我们的主App组件,并通过React Router Dom将其连接到Users和User组件。

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Users from './components/Users';
import User from './components/User';

const App = () => (
  <Router>
    <Switch>
      <Route exact path="/users" component={Users} />
      <Route exact path="/users/:id" component={User} />
    </Switch>
  </Router>
);

export default App;
  1. 渲染应用

最后,我们在src/index.js中渲染我们构建的应用。将我们的App组件包裹在Redux和Redux-Thunk Provider中,以便使用我们的store和操作。

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

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

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

这样,我们的应用就完成了,并拥有了基本的增删改查使用。

结论

在本文中,我们演示了使用React和Redux创建一个简单的应用程序的步骤。我们学习了如何创建Reducer、Action和React组件来处理我的数据和逻辑。对于React和Redux的初学者而言,这种构建方式可以使我们更好地理解如何使用这两个库。