107
Code
const addPerson = (event) => {
// event.preventDefault();
const personObject = {
name: newName,
id: persons.length + 1,
number: newNumber,
};
if (persons.some((person) => person.name === newName)) {
if (
window.confirm(
`${newName} is already in the phonebook, replace the old number with new one? `
)
) {
const person = persons.find((person) => person.name === newName);
const updatedPerson = { ...person, number: newNumber };
axios
.put(`http://localhost:3002/persons/${person.id}`, updatedPerson)
.then((response) => {
const responseData = response.data;
setPersons((prev) =>
prev.map((p) => (p.id === person.id ? responseData : p))
);
});
}
} else {
axios
.post("http://localhost:3002/persons", personObject)
.then((response) => {
const responseData = response.data;
setPersons(responseData.concat(personObject));
setNewName("");
setNewNumber("");
});
}
功能描述
这是一个 React 电话簿应用中的添加/更新联系人功能,支持新增联系人或更新已存在联系人的电话号码。
核心逻辑流程
创建联系人对象
- 使用表单输入的
newName
和newNumber
创建personObject
- ID 自动设置为
persons.length + 1
- 使用表单输入的
检查重名逻辑
persons.some((person) => person.name === newName);
- 如果名字已存在 → 进入更新流程
- 如果名字不存在 → 进入新增流程
更新流程(重名时)
- 弹出确认对话框
window.confirm()
- 用户确认后:
- 用
find()
找到原有联系人 - 创建
updatedPerson
对象(保留 id,更新 number) - 发送 PUT 请求到
http://localhost:3002/persons/{id}
- 更新成功后,用
map()
更新本地 state
- 用
- 弹出确认对话框
新增流程(无重名)
- 发送 POST 请求到
http://localhost:3002/persons
- 成功后:
- 将新数据合并到
persons
数组 - 清空表单输入
setNewName("")
和setNewNumber("")
- 将新数据合并到
- 发送 POST 请求到
涉及知识点
1. JavaScript 数组方法
Array.some()
: 检查数组中是否有元素满足条件(返回 boolean)Array.find()
: 查找第一个满足条件的元素(返回元素本身)Array.map()
: 遍历数组并返回新数组,用于不可变更新 stateArray.concat()
: 合并数组,返回新数组
2. React 核心概念
- State 管理:
setPersons
更新联系人列表 - 不可变性原则:
- 使用展开运算符
{ ...person, number: newNumber }
- 使用
map()
而不是直接修改数组
- 使用展开运算符
- 函数式 setState:
setPersons((prev) => ...)
,基于前一个状态更新
3. HTTP 请求(axios)
- POST 请求: 创建新资源
axios.post(url, data).then(response => {...})
- PUT 请求: 更新已有资源
axios.put(url, updatedData).then(response => {...})
- Promise 处理: 使用
.then()
处理异步响应
4. 用户交互
window.confirm()
: 浏览器原生确认对话框- 条件渲染/执行: 根据用户选择执行不同逻辑
5. ES6+ 语法
- 箭头函数:
(event) => {...}
- 模板字符串:
`${newName} is already...`
- 对象展开运算符:
{ ...person, number: newNumber }
- 三元运算符(隐含):
map()
中的条件判断p.id === person.id ? responseData : p
潜在问题/改进点
⚠️ ID 生成方式不安全
id: persons.length + 1
在删除联系人后可能导致 ID 冲突- 应该由后端生成或使用 UUID
⚠️ 新增流程的数据冗余
- 第 42 行:
responseData.concat(personObject)
- 应该只使用服务器返回的数据:
setPersons(persons.concat(responseData))
- 第 42 行:
⚠️ 缺少错误处理
- 没有
.catch()
处理请求失败的情况
- 没有
⚠️ 注释掉的代码
// event.preventDefault();
被注释,可能导致表单提交时页面刷新
关键代码片段解析
对象更新(不可变方式)
const updatedPerson = { ...person, number: newNumber };
创建新对象,保留原有属性,只更新 number
字段
State 的条件更新
setPersons((prev) => prev.map((p) => (p.id === person.id ? responseData : p)));
只更新匹配 ID 的元素,其他元素保持不变