react 项目实战(七)用户编辑与删除

添加操作列

编辑与删除功能都是针对已存在的某一个用户执行的操作,所以在用户列表中需要再加一个“操作”列来展现【编辑】与【删除】这两个按钮。

修改/src/pages/UserList.js文件,添加方法handleEdithandleDel,并在table中添加一列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
...
class UserList extends React.Component {
  constructor (props) { ... }
 
  componentWillMount () { ... }
  // 编辑
  handleEdit (user) {
 
  }
  // 删除
  handleDel (user) {
 
  }
 
  render () {
    const {userList} = this.state;
 
    return (
      <HomeLayout title="用户列表">
        <table>
          <thead>
          <tr>
            <th>用户ID</th>
            <th>用户名</th>
            <th>性别</th>
            <th>年龄</th>
            <th>操作</th>
          </tr>
          </thead>
 
          <tbody>
          {
            userList.map((user) => {
              return (
                <tr key={user.id}>
                  <td>{user.id}</td>
                  <td>{user.name}</td>
                  <td>{user.gender}</td>
                  <td>{user.age}</td>
                  <td>
                    <a href="javascript:void(0)" onClick={() => this.handleEdit(user)}>编辑</a>
                      
                    <a href="javascript:void(0)" onClick={() => this.handleDel(user)}>删除</a>
                  </td>
                </tr>
              );
            })
          }
          </tbody>
        </table>
      </HomeLayout>
    );
  }
}
...

点击编辑(删除)时,会把该行的user对象作为参数传给handleEdit(handleDel)方法,在handleEdit(handleDel)方法中我们就可以根据传入的user对象进行相应的操作了。

用户删除

用户删除比较简单,先解决它。

在执行删除数据的操作时,通常需要对操作进行进一步的确认以避免误删数据酿成惨剧。

所以在handleDel方法中我们应该先确认用户是否想要执行删除操作,在用户确认后调用删除用户的接口来删除用户:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
// 删除
handleDel (user) {
  const confirmed = window.confirm(`确定要删除用户 ${user.name} 吗?`); // confirm 无法识别,需要加 window.
 
  if (confirmed) {
    fetch('http://localhost:8000/user/' + user.id, {
      method: 'delete'
    })
    .then(res => res.json())
    .then(res => {
      this.setState({
        userList: this.state.userList.filter(item => item.id !== user.id)
      });
      alert('删除用户成功');
    })
    .catch(err => {
      console.error(err);
      alert('删除用户失败');
    });
  }
}
...

用户编辑

用户编辑和用户添加基本上是一样的,不同的地方有:

  • 用户编辑需要将用户的数据先填充到表单
  • 用户编辑在提交表单的时候调用的接口和方法不同
  • 页面标题不同
  • 页面路由不同

那么我们可以复制UserAdd.js文件的代码到一个新的UserEdit.js文件中,再对上述四点进行修改…吗?

当然不行!在前文中我们费尽心思对重复代码进行优化,更不能为了偷懒直接复制代码完事啦。

想办法让原来的代码既能够支持添加操作又能够支持编辑操作!

为了达到这一个目标,我们需要:

  • 升级formProvider使其返回的表单组件支持传入表单的值(用于主动填充表单)
  • 将UserAdd.js中的大部分代码抽离到一个通用组件UserEditor,通过传入不同的props来控制组件的行为是添加还是编辑

升级formProvider

修改/src/utils/formProvider.js文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function formProvider (fields) {
  return function (Comp) {
    ...
    class FormComponent extends React.Component {
      constructor (props) {
        ...
        this.setFormValues = this.setFormValues.bind(this);
      }
 
      setFormValues (values) {
        if (!values) {
          return;
        }
 
        const {form} = this.state;
        let newForm = {...form};
        for (const field in form) {
          if (form.hasOwnProperty(field)) {
            if (typeof values[field] !== 'undefined') {
              newForm[field] = {...newForm[field], value: values[field]};
            }
            // 正常情况下主动设置的每个字段一定是有效的
            newForm[field].valid = true;
          }
        }
 
        this.setState({form: newForm});
      }
 
      handleValueChange (fieldName, value) { ... }
 
      render () {
        const {form, formValid} = this.state;
        return (
          <Comp
            {...this.props}
            form={form}
            formValid={formValid}
            onFormChange={this.handleValueChange}
            setFormValues={this.setFormValues}
          />
        );
      }
    }
 
    return FormComponent;
  }
}
...

给表单组件传入了一个setFormValues的方法,用于在组件中主动设置表单的值。

完整代码(高阶组件):

src / utils / formProvider.js

抽离UserEditor

接下来新建/src/components/UserEditor.js文件,将表单处理代码从UserAdd.js里搬过去(省略号部分与原来的代码相同):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import React from 'react';
import FormItem from '../components/FormItem'; // 或者写成 ./FormItem
import formProvider from '../utils/formProvider';
// 引入 prop-types
import PropTypes from 'prop-types';
 
class UserEditor extends React.Component {
  handleSubmit (e) { ... }
 
  render () {
    const {form: {name, age, gender}, onFormChange} = this.props;
    return (
      <form onSubmit={(e) => this.handleSubmit(e)}>
        ...
      </form>
    );
  }
}
 
 
UserEditor.contextTypes = {
  router: PropTypes.object.isRequired
};
 
UserEditor = formProvider({ ... })(UserEditor);
 
export default UserEditor;

然后再handleSubmit方法中,通过检查是否收到一个editTargetprops来判断这次的操作是添加操作还是编辑操作,并根据当前的操作切换调用接口的url和method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
...
handleSubmit (e) {
  e.preventDefault();
 
  const {form: {name, age, gender}, formValid, editTarget} = this.props;
  if (!formValid) {
    alert('请填写正确的信息后重试');
    return;
  }
 
  let editType = '添加';
  let apiUrl = 'http://localhost:8000/user';
  let method = 'post';
  if (editTarget) {
    editType = '编辑';
    apiUrl += '/' + editTarget.id;
    method = 'put';
  }
 
  fetch(apiUrl, {
    method,
    body: JSON.stringify({
      name: name.value,
      age: age.value,
      gender: gender.value
    }),
    headers: {
      'Content-Type': 'application/json'
    }
  })
    .then((res) => res.json())
    .then((res) => {
      if (res.id) {
        alert(editType + '用户成功');
        this.context.router.push('/user/list');
        return;
      } else {
        alert(editType + '失败');
      }
    })
    .catch((err) => console.error(err));
}
...

同时,我们也需要在UserEditor加载的时候检查是否存在props.editTarget,如果存在,使用props.setFormValues方法将editTarget的值设置到表单:

1
2
3
4
5
6
7
8
...
componentWillMount () {
  const {editTarget, setFormValues} = this.props;
  if (editTarget) {
    setFormValues(editTarget);
  }
}
...

这样我们的UserEditor就基本完成了,当我们要作为一个用户添加器使用时,只需要:

1
2
3
...
  <UserEditor/>
...

而作为一个用户编辑器使用时,则需要将编辑的目标用户对象传给editTarget这个属性:

1
2
3
...
 <UserEditor editTarget={user}/>
...

完成代码(编辑器组件):

src / components / UserEditor.js

所以现在就可以将UserAdd.js文件改成这样了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * 用户添加页面
 */
import React from 'react';
// 布局组件
import HomeLayout from '../layouts/HomeLayout';
// 编辑组件
import UserEditor from '../components/UserEditor';
 
class UserAdd extends React.Component {
  render() {
    return (
      <HomeLayout title="添加用户">
        <UserEditor />
      </HomeLayout>
    );
  }
}
 
export default UserAdd;

添加UserEditPage

现在需要添加一个/src/pages/UserEdit.js文件作为编辑用户的页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
 * 编辑用户页面
 */
import React from 'react';
// 布局组件
import HomeLayout from '../layouts/HomeLayout';
// 引入 prop-types
import PropTypes from 'prop-types';
// 编辑组件
import UserEditor from '../components/UserEditor';
 
class UserEdit extends React.Component {
  // 构造器
  constructor(props) {
    super(props);
    // 定义初始化状态
    this.state = {
      user: null
    };
  }
 
  // 生命周期--组件加载中
  componentWillMount(){
    // 定义常量
    const userId = this.context.router.params.id;
    /**
     * 发送请求
     * 获取用户数据
     */
    fetch('http://localhost:8000/user/' + userId)
    .then(res => res.json())
    .then(res => {
      this.setState({
        user: res
      });
    })
  }
 
  render() {
    const {user} = this.state;
    return (
      <HomeLayout title="编辑用户">
        {
          user ? <UserEditor editTarget={user} /> : '加载中...'
        }
      </HomeLayout>
    );
  }
}
 
UserEdit.contextTypes = {
  router: PropTypes.object.isRequired
};
 
export default UserEdit;

在这个页面组件里,我们根据路由中名为id的参数(this.context.router.params.id)来调用接口获取用户数据(保存在this.state.user中)。

当user数据未就绪时,我们不应该展示出编辑器以避免用户混乱或者误操作:使用三元运算符,当this.state.user有值时渲染UserEditor组件,否则显示文本“加载中…”。

注意:任何使用this.context.xxx的地方,必须在组件的contextTypes里定义对应的PropTypes

别忘了在/src/index.js中给页面添加路由,路由的path中使用:id来定义路由的参数(参数名与页面组件中获取参数时的参数名相对应):

1
2
3
4
5
6
7
8
import UserEditPage from './pages/UserEdit'; // 用户编辑页面
 
ReactDOM.render((
  <Router history={hashHistory}>
    ...
    <Route path="/user/edit/:id" component={UserEditPage}/>
  </Router>
), document.getElementById('root'));

完成handleEdit方法

最后,来补上UserList页面组件的handleEdit方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import PropTypes from 'prop-types';
 
class UserList extends React.Component {
  constructor (props) { ... }
 
  componentWillMount () { ... }
  /**
   * 编辑
   */
  handleEdit (user) {
    // 跳转编辑页面
    this.context.router.push('/user/edit/' + user.id);
  }
 
  handleDel (user) { ... }
 
/**
 * 任何使用this.context.xxx的地方,必须在组件的contextTypes里定义对应的PropTypes
 */
UserList.contextTypes = {
  router: PropTypes.object.isRequired
};

在handleEdit方法中只需要使用router.push方法跳转到该用户的编辑页面,别忘了加上contextTypes

项目目录:

posted @   每天都要进步一点点  阅读(2433)  评论(0编辑  收藏  举报
编辑推荐:
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
阅读排行:
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
· 卧槽!C 语言宏定义原来可以玩出这些花样?高手必看!
· langchain0.3教程:从0到1打造一个智能聊天机器人
点击右上角即可分享
微信分享提示