如何在React Admin中渲染Firestore的自定义JSON嵌套数据
问题描述
我的数据来自Firestore数据库,此前基于Firestore测试集合创建多个Resource,运行效果良好。目前遇到问题:某集合仅含单个文档,其snapshot字段存储大体积JSON字符串,数据结构如下:
[ { "id": "rj2PK96j3MashOA1AVFh", "timestamp": "2023-11-15T15:26:18.628Z", "snapshot": "{\"guests\": [{\"id\":\"41090\",\"id_transaction\":\"41755\",\"id_people\":\"33100\",\"status\":\"checkin\",\"paid_days_in_advance_to_partner\":\"2\",\"nights_to_pay\":\"13\",\"id_bed\":\"87\",\"id_key\":\"88\",\"room_type_id\":\"1\",\"original_checkout_plan\":\"2023-10-02\",\"checkout_plan\":\"2023-10-13\",\"checkout_confirmed\":{}}}", "updated": "2023-11-02T15:39:48.536Z", "domain": "testing" } ]
使用常规List/Datagrid/TextField组件可访问所有值,包括在React Admin列表中显示字符串化JSON,但希望snapshot字段渲染每个JSON键值对并保留组件能力。尝试解析snapshot后遍历guest用TextField渲染,控制台能获取数据但界面无内容,代码如下:
const SnapshotList = () => { const { data, resource } = useListContext(); const snapshotString = data[0].snapshot; try { const snapshotJson = JSON.parse(snapshotString); const todaysGuests = snapshotJson.assign_today.guests; console.log("Todays Guests:", todaysGuests); return ( <Datagrid> {todaysGuests.map((guest: any, index: any) => ( <React.Fragment key={index}> <TextField label="Arrival Date" source={`${guest}.arrival_date`} /> <TextField label="Reservation Code" source={`${guest}.reservation_code`} /> <TextField label="Given Name" source={`${guest}.given_name`} /> </React.Fragment> ))} </Datagrid> ); } catch (error) { console.error("Error parsing JSON:", error); return null; } };
export const Snapshots = () => ( <ResourceContextProvider value="snapshots"> <List pagination={<Pagination perPage={1000} rowsPerPageOptions={[]} />}> <SnapshotList /> </List> </ResourceContextProvider> );
请问如何通过传递自定义资源给TextField组件生成对应Datagrid?
解决方案
问题核心在于React Admin的Datagrid和TextField的工作逻辑:Datagrid会自动遍历当前ListContext中的data数组生成行,TextField的source是当前行数据的属性路径,而非直接传入对象。以下是两种可行方案:
方案1:替换ListContext的数据源为解析后的guest数组
直接将解析出的todaysGuests作为新数据传入Datagrid,通过覆盖ListContext实现:
import { useListContext, Datagrid, TextField, ListContextProvider } from 'react-admin'; const SnapshotList = () => { const { data, ...restContext } = useListContext(); try { const snapshotString = data[0].snapshot; const snapshotJson = JSON.parse(snapshotString); const todaysGuests = snapshotJson.guests; // 注意:需匹配实际JSON结构,示例中无assign_today层级 console.log("Todays Guests:", todaysGuests); return ( <ListContextProvider value={{ ...restContext, data: todaysGuests }}> <Datagrid> <TextField label="客人ID" source="id" /> <TextField label="交易ID" source="id_transaction" /> <TextField label="入住状态" source="status" /> <TextField label="需付晚数" source="nights_to_pay" /> <TextField label="原退房计划" source="original_checkout_plan" /> </Datagrid> </ListContextProvider> ); } catch (error) { console.error("JSON解析错误:", error); return null; } };
方案2:手动渲染表格结构
如果不需要复用Datagrid的默认逻辑,可直接遍历解析后的guest数据自定义表格:
const SnapshotList = () => { const { data } = useListContext(); try { const snapshotString = data[0].snapshot; const snapshotJson = JSON.parse(snapshotString); const todaysGuests = snapshotJson.guests; return ( <table style={{width: '100%', borderCollapse: 'collapse'}}> <thead> <tr style={{borderBottom: '1px solid #ddd'}}> <th style={{padding: '8px', textAlign: 'left'}}>客人ID</th> <th style={{padding: '8px', textAlign: 'left'}}>交易ID</th> <th style={{padding: '8px', textAlign: 'left'}}>入住状态</th> <th style={{padding: '8px', textAlign: 'left'}}>需付晚数</th> </tr> </thead> <tbody> {todaysGuests.map((guest, index) => ( <tr key={index} style={{borderBottom: '1px solid #ddd'}}> <td style={{padding: '8px'}}>{guest.id}</td> <td style={{padding: '8px'}}>{guest.id_transaction}</td> <td style={{padding: '8px'}}>{guest.status}</td> <td style={{padding: '8px'}}>{guest.nights_to_pay}</td> </tr> ))} </tbody> </table> ); } catch (error) { console.error("JSON解析错误:", error); return null; } };
关键注意事项
- 数据结构匹配:示例
snapshot中直接是guests数组,需根据实际JSON结构调整解析路径,若确实存在assign_today层级则保留snapshotJson.assign_today.guests - TextField的source属性:使用
Datagrid时,source是当前行数据的属性名,例如source="id"对应guest.id,无需拼接对象路径 - ListContext的作用:
Datagrid依赖ListContext提供的data,替换上下文数据即可让Datagrid自动遍历guest数组
内容的提问来源于stack exchange,提问作者RodrigoCastro
相关产品推荐
相关产品推荐

