React子组件5x5热力图网格:基于State动态分配单元格编号问询
Hey there! I totally get where you're coming from—switching from jQuery's DOM-manipulation mindset to React's declarative, data-driven approach can feel tricky at first. Let's walk through a clean, maintainable way to build this heatmap without messy ternary chains or direct DOM tweaks.
First: Shift Your Mindset
Instead of targeting <td> elements by ID and updating their innerHTML, we'll let our data define what renders in each cell. React works best when you structure your data to match the UI you want, then map over that data to generate elements.
Step 1: Structure Your riskData for Easy Access
First, let's assume your parent component passes riskData as an array of objects like this (adjust if your structure is different):
// Example riskData from parent const riskData = [ { col: 4, row: 5, id: "R1" }, { col: 4, row: 5, id: "R2" }, { col: 2, row: 3, id: "R3" }, // ... more entries ];
We'll pre-process this data into a grouped structure where each key is a row-col pair, and the value is an array of IDs for that cell. This way, when we render each cell, we can instantly grab all relevant IDs without filtering the entire array every time.
Use useMemo to cache this grouped data (so it only re-calculates when riskData changes):
import { useMemo } from 'react'; function HeatmapGrid({ riskData }) { // Group data by row-col pairs const groupedRiskData = useMemo(() => { return riskData.reduce((acc, item) => { const cellKey = `${item.row}-${item.col}`; // Add the ID to the cell's array (or create a new array if it doesn't exist) acc[cellKey] = [...(acc[cellKey] || []), item.id]; return acc; }, {}); }, [riskData]); // ... rest of component }
Step 2: Render the 5x5 Grid
Now we'll generate 5 rows, each with 5 columns. For each cell, we'll look up its IDs from our grouped data and render them.
This avoids complex ternary logic—we just check if the cell has IDs, then render them (or leave it empty if not):
function HeatmapGrid({ riskData }) { // ... groupedRiskData code from above return ( <table className="heatmap-table"> {/* Generate 5 rows (row numbers 1-5) */} {Array.from({ length: 5 }, (_, rowIdx) => { const rowNumber = rowIdx + 1; return ( <tr key={`row-${rowNumber}`}> {/* Generate 5 columns (column numbers 1-5) */} {Array.from({ length: 5 }, (_, colIdx) => { const colNumber = colIdx + 1; const cellKey = `${rowNumber}-${colNumber}`; const cellIds = groupedRiskData[cellKey] || []; return ( <td key={`cell-${rowNumber}-${colNumber}`} className={`heatmap-cell ${cellIds.length > 0 ? 'has-risks' : ''}`} > {/* Render all IDs for this cell */} {cellIds.map(id => ( <span key={id} className="risk-id">{id}</span> ))} {/* Alternatively, if you want comma-separated text: {cellIds.join(', ')} */} </td> ); })} </tr> ); })} </table> ); }
Step 3: Add Heatmap Styling (Optional)
Since it's a heatmap, you can dynamically adjust the cell's style based on the number of IDs (or a risk level if your data includes it). For example:
.heatmap-table { border-collapse: collapse; } .heatmap-cell { width: 60px; height: 60px; border: 1px solid #ddd; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; padding: 4px; } /* Color cells based on number of risks */ .heatmap-cell.has-risks { background-color: #fff3cd; } .heatmap-cell.has-risks:has(.risk-id:nth-child(2)) { background-color: #ffecb5; } .heatmap-cell.has-risks:has(.risk-id:nth-child(3)) { background-color: #ffd495; }
Why This Works Better Than jQuery
- Declarative: You describe what should render based on data, not how to manipulate the DOM.
- Maintainable: If you need to change how cells look or behave, you only modify the data structure or render logic, not DOM selectors.
- Performant: Using
useMemoensures we don't re-process the data on every render, and we avoid filtering the entireriskDataarray for each cell.
Bonus: Clean Up With a Custom Hook
If you use this grouping logic elsewhere, extract it into a custom hook to keep your component tidy:
function useGroupedRiskData(riskData) { return useMemo(() => { return riskData.reduce((acc, item) => { const cellKey = `${item.row}-${item.col}`; acc[cellKey] = [...(acc[cellKey] || []), item.id]; return acc; }, {}); }, [riskData]); } // Then use it in your component: const groupedRiskData = useGroupedRiskData(riskData);
内容的提问来源于stack exchange,提问作者dmc91

