React JS中使用Hooks实现模态框提交成功后追加HTML
现有代码问题点
- 请求头配置错误:
Access-Control-Allow-Origin是服务端返回的响应头,前端手动添加无效;FormData类型的请求浏览器会自动生成带分隔符的Content-Type,手动写死multipart/form-data会丢失分隔符导致上传失败。 - 渲染逻辑不符合React设计:你尝试往数组里push原生HTML字符串的写法是原生JS的DOM操作思路,React是数据驱动视图,且你代码中
append变量没有通过useState声明为响应式状态,即使push内容也不会触发页面更新。 - 硬编码HTML存在ID重复问题:你写死了所有折叠面板、按钮的ID,多次追加内容后会出现ID冲突,导致折叠、点击等功能失效。
- 异常处理缺失:catch块直接抛出错误没有用户提示,提交失败时用户无感知。
React Hooks 实现方案
1. 声明响应式状态
用useState管理清单项列表、表单字段、模态框显隐状态:
import { useState } from 'react'; import { toast } from 'react-toastify'; // 组件内部 // 清单项列表,初始值可替换为接口拉取的已有数据 const [checklist, setChecklist] = useState([]); const [name, setName] = useState(''); const [listCategory, setListCategory] = useState(''); const [modalVisible, setModalVisible] = useState(false);
2. 修正提交逻辑
去掉错误的请求头,提交成功后通过更新状态触发视图渲染,优先使用接口返回的真实数据:
const handleSubmit = async (e) => { e.preventDefault(); const formData = new FormData(); formData.append('name', name); formData.append('list_category', listCategory); formData.append('created_by', '629829078779cc4a00139c9a'); try { const { data } = await api.post('checklist', formData); if (data.status === '1') { // 新条目追加到列表状态,用接口返回的唯一ID避免重复 setChecklist(prevList => [ ...prevList, { id: data.data.id, title: data.data.name || name, tasks: data.data.tasks || [ { id: 1, content: 'Reserve tables for the event date', done: false }, { id: 2, content: 'Rent chairs and choose chair style', done: false }, { id: 3, content: 'Rent microphone', done: false } ] } ]); // 重置表单、提示成功、关闭模态框 setName(''); setListCategory(''); toast.success(data.message); setModalVisible(false); } else { toast.error(data.message.errors.name.message); } } catch (err) { toast.error('提交失败,请稍后重试'); console.error(err); } };
3. JSX渲染逻辑
不要拼接HTML字符串,通过遍历状态渲染列表,动态生成唯一ID避免冲突:
return ( <div className="page-wrapper"> {/* 页面其他内容,比如唤起模态框的按钮 */} <button className="btn btn-primary" onClick={() => setModalVisible(true)}> 新建检查清单 </button> {/* 清单列表容器 */} <div id="accordionExample"> {checklist.map(item => { const headingId = `heading-${item.id}`; const collapseId = `collapse-${item.id}`; return ( <div className="item" key={item.id}> <div className="item-header" id={headingId}> <h2 className="mb-0"> <button className="btn btn-link" type="button" data-toggle="collapse" data-target={`#${collapseId}`} aria-expanded="true" aria-controls={collapseId} > <span className="title_heading">{item.title}</span> <div className="right90" id={item.id}> <a href="/">Add new task</a> <i className="fa fa-angle-down"></i> <img src="Assets/img/close_icon.png" alt="关闭" /> </div> </button> </h2> </div> <div id={collapseId} className="collapse show" aria-labelledby={headingId} data-parent="#accordionExample" > <div className="t-p"> <div className="table-responsive"> <table className="table custom_table"> <thead> <tr> <th> </th> <th>Done</th> <th>Task</th> <th> </th> <th> </th> </tr> </thead> <tbody> {item.tasks.map(task => ( <tr key={task.id}> <td className="custom_ty6"> <img src="Assets/img/delete.png" alt="删除任务" /> </td> <td className="cuts8"> <label className="container99"> <input type="checkbox" defaultChecked={task.done} /> <span className="checkmark"></span> </label> </td> <td className="custom_labe8"> <input type="text" placeholder={task.content} /> <img src="Assets/img/edit.png" alt="编辑任务" /> </td> <td> <div className="cutom_plus"> <img src="Assets/img/plus.png" alt="分配" /> Assign task </div> </td> <td> <div className="cutom_plus"> <img src="Assets/img/plus.png" alt="设置日期" /> Due Date </div> </td> </tr> ))} </tbody> </table> </div> </div> </div> </div> ) })} </div> {/* 提交模态框 */} {modalVisible && ( <div className="modal"> <form onSubmit={handleSubmit}> {/* 表单字段,绑定value和onChange */} <input type="text" value={name} onChange={e => setName(e.target.value)} placeholder="请输入清单名称" /> <div className="modal-footer"> <button type="button" onClick={() => setModalVisible(false)}>取消</button> <button type="submit">确认提交</button> </div> </form> </div> )} </div> )
特殊场景说明
如果必须渲染外部传入的原生HTML字符串(存在XSS注入风险,非必要不使用),需要通过dangerouslySetInnerHTML属性渲染,且必须提前做恶意代码过滤:
// 不推荐使用示例 const [htmlList, setHtmlList] = useState([]); const appendData = () => { setHtmlList(prev => [...prev, /* 你的HTML字符串 */]) } // 渲染时 {htmlList.map((html, idx) => ( <div key={idx} dangerouslySetInnerHTML={{__html: html}} /> ))}
内容的提问来源于stack exchange,提问作者jandial nidhi
相关产品推荐
相关产品推荐

