如何在React JSON Schema Form数组的每个项中添加备注/笔记按钮
如何在React JSON Schema Form数组的每个项中添加备注/笔记按钮
嘿,我来帮你搞定这个需求!要给RJSF数组的每个项加上备注功能,得兼顾UI交互和数据关联——既要让用户能点击按钮调出输入框,还要确保笔记能和对应项绑定、一起保存。下面是具体的实现思路和代码修改方案:
核心思路
RJSF是Schema驱动的表单库,最稳妥的方式是调整Schema结构:把原来单一的数组项(比如JWT字符串)改成对象,这样就能在每个项里同时存原数据和备注。再通过自定义字段组件添加按钮、控制备注输入框的显示/隐藏,完美适配你的需求。
具体修改步骤
1. 调整Auth Schema的结构
先把数组项从string改成包含原数据和备注的对象,以authorization为例:
{ id: 'authorization', title: 'Authorization', schema: { type: 'array', items: { type: 'object', properties: { token: { type: 'string', pattern: '^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$', title: 'JWT Token' }, note: { type: 'string', title: 'Note', description: 'Add a memo for this token' } }, required: ['token'] // token必填,note可选 }, }, uiSchema: { items: { 'ui:field': TokenWithNoteField, // 指定自定义字段组件 }, }, }
account项也可以用同样逻辑,给每个account对象添加note字段。
2. 自定义数组项组件(带备注按钮)
创建一个组件,用来渲染每个数组项,包含原数据输入框、"Add Note"按钮和备注输入框:
const TokenWithNoteField = (props) => { const { formData, onChange, schema, uiSchema } = props; const [showNoteInput, setShowNoteInput] = useState(false); // 同步token输入变化到formData const handleTokenChange = (newTokenValue) => { onChange({ ...(formData || {}), token: newTokenValue }); }; // 同步备注输入变化到formData const handleNoteChange = (newNoteValue) => { onChange({ ...(formData || {}), note: newNoteValue }); }; return ( <div style={{ marginBottom: '16px', padding: '8px', border: '1px solid #f0f0f0', borderRadius: '4px' }}> {/* 渲染Token的textarea输入框 */} <RjsfForm.Field name={`${props.name}.token`} schema={schema.properties.token} uiSchema={{ 'ui:widget': 'textarea', 'ui:placeholder': 'Paste JWT Token here' }} formData={formData?.token} onChange={handleTokenChange} /> {/* 切换备注框显示/隐藏的按钮 */} <Button type="text" onClick={() => setShowNoteInput(!showNoteInput)} style={{ margin: '8px 0' }} > {showNoteInput ? 'Hide Note' : 'Add Note'} </Button> {/* 备注输入框,默认隐藏 */} {showNoteInput && ( <RjsfForm.Field name={`${props.name}.note`} schema={schema.properties.note} uiSchema={{ 'ui:placeholder': 'Write a note about this token...' }} formData={formData?.note} onChange={handleNoteChange} /> )} </div> ); };
这个组件会处理每个项的UI状态(备注框显隐),同时自动把token和note的变化同步到formData中。
3. 适配数据加载和保存
- 加载数据:如果数据库里原来存的是纯token数组(比如
["token1", "token2"]),在useEffect取数据后要转换成对象数组:useEffect(() => { const fetchData = async () => { // 假设从数据库拿到原始string数组 const rawData = await fetchAuthData(auth.id, source.id); // 转换为带空note的对象数组 const formattedData = rawData?.map(token => ({ token, note: '' })) || []; setFormData(formattedData); }; fetchData(); }, [auth.id, source.id]); - 保存数据:
handleSubmit里的formData已经包含每个项的token和note,直接保存到数据库即可——它们本来就是同一个对象的属性,关联关系天然存在。
4. 完整整合后的代码片段
把上面的内容整合到现有代码中,关键部分大概是这样:
// 先定义自定义组件 const TokenWithNoteField = (props) => { const { formData, onChange, schema, uiSchema } = props; const [showNoteInput, setShowNoteInput] = useState(false); const handleTokenChange = (newTokenValue) => { onChange({ ...(formData || {}), token: newTokenValue }); }; const handleNoteChange = (newNoteValue) => { onChange({ ...(formData || {}), note: newNoteValue }); }; return ( <div style={{ marginBottom: '16px', padding: '8px', border: '1px solid #f0f0f0', borderRadius: '4px' }}> <RjsfForm.Field name={`${props.name}.token`} schema={schema.properties.token} uiSchema={{ 'ui:widget': 'textarea', 'ui:placeholder': 'Paste JWT Token here' }} formData={formData?.token} onChange={handleTokenChange} /> <Button type="text" onClick={() => setShowNoteInput(!showNoteInput)} style={{ margin: '8px 0' }} > {showNoteInput ? 'Hide Note' : 'Add Note'} </Button> {showNoteInput && ( <RjsfForm.Field name={`${props.name}.note`} schema={schema.properties.note} uiSchema={{ 'ui:placeholder': 'Write a note about this token...' }} formData={formData?.note} onChange={handleNoteChange} /> )} </div> ); }; // 修改后的authSchema const auth = { authSchema: [ { id: 'authorization', title: 'Authorization', schema: { type: 'array', items: { type: 'object', properties: { token: { type: 'string', pattern: '^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$', title: 'JWT Token' }, note: { type: 'string', title: 'Note', description: 'Add a memo for this token' } }, required: ['token'], }, }, uiSchema: { items: { 'ui:field': TokenWithNoteField, }, }, }, // account项也可类似修改,添加note字段 { id: 'account', title: 'Account', schema: { type: 'array', items: { type: 'object', properties: { username: { type: 'string', title: 'Username', minLength: 5, pattern: '^[a-z0-9]+$', }, password: { type: 'string', title: 'Password', minLength: 8, }, note: { type: 'string', title: 'Account Note', } }, required: ['username', 'password'], }, }, uiSchema: { items: { password: { 'ui:widget': 'password', }, }, }, }, ] }; // TabItem组件调整数据加载逻辑 function TabItem({source, auth}: TabItemProps): JSX.Element{ const [formData, setFormData] = useState(); const handleSubmit = async (id: string, event: any): Promise<void> => { // 直接保存包含note的formData到数据库 await saveAuthData(id, source.id, event.formData); }; useEffect(() => { const fetchData = async () => { const rawData = await getAuthData(auth.id, source.id); if (auth.id === 'authorization') { // 转换为带note的对象数组 const formatted = rawData?.map(t => ({ token: t, note: '' })) || []; setFormData(formatted); } else if (auth.id === 'account') { // 给现有account项补全note字段 const formatted = rawData?.map(item => ({ ...item, note: item.note || '' })) || []; setFormData(formatted); } else { setFormData(rawData); } }; fetchData(); }, [auth.id, source.id]); return ( <RjsfForm formData={formData} focusOnFirstError={true} liveValidate={true} schema={auth.schema} uiSchema={auth.uiSchema} validator={validator} onSubmit={(e) => handleSubmit(auth.id, e)} showErrorList={false} /> ); }
常见疑问解答
- 为什么要修改Schema?:RJSF是Schema驱动的,所有需要持久化的数据都应该定义在Schema里,这样formData会自动包含这些字段,不需要手动维护关联关系,保存和加载都更省心。
- 能不能不修改Schema?:理论上可以用自定义模板添加UI元素,但笔记数据无法和formData绑定,得自己手动管理状态和关联,后续维护成本很高,不推荐。
备注:内容来源于stack exchange,提问作者VQH DEV
相关产品推荐
相关产品推荐

