I have a nested array like
"datasets":{
"type":"array",
"default":[
{
"name":"Main",
"data":[
{
"Department":"Sheriff",
"Budget":100000,
"MeetAt":"2025-01-26T14:30:00Z",
"preferredColor":"red",
"PostContent":"<div> </div>"
},
{
"Department":"Assessor",
"Budget":20000,
"MeetAt":"2025-01-26T14:30:00Z",
"preferredColor":"#232323",
"PostContent":"<div> </div>"
},
{
"Department":"Treasurer",
"Budget":30000,
"MeetAt":"2025-01-26T14:30:00Z",
"preferredColor":"#E72323",
"PostContent":"<div> </div>"
}
]
},
{
"name":"Locations",
"data":[
{
"State":"California",
"Coordinates":"",
"MeetAt":"2025-01-26T14:30:00Z",
"PreferredColor":"#e0e0e0",
"PostContent":"<div> </div>"
},
{
"State":"Texas",
"Coordinates":"",
"MeetAt":"2025-01-26T14:30:00Z",
"PreferredColor":"#e0e0e0",
"PostContent":"<div> </div>"
},
{
"State":"Florida",
"Coordinates":"",
"MeetAt":"2025-01-26T14:30:00Z",
"PreferredColor":"#e0e0e0",
"PostContent":"<div> </div>"
}
]
}
]
},
And then I have a component that renders child components called with one being rendered for each object inside the ‘datasets’.
import { useState, useEffect } from '@wordpress/element';
import { Button, Modal } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import LCPDataGrid from './LCPDataGrid';
const LCPDatasetBuilder = ({ attributes, setAttributes }) => {
const [isOpen, setIsOpen] = useState(false);
const [activeTab, setActiveTab] = useState(0); // Track the active tab
const handleUpdateDataset = (index, newData) => {
console.log("newData (formatted for copy-pasting):", JSON.stringify(newData, null, 2));
// Check if newData.data is an array
if (Array.isArray(newData.data)) {
// Create a deep copy of the datasets to avoid mutating the state directly
const datasets = [...attributes.datasets];
// Check if the dataset at the specified index exists
if (datasets[index]) {
// Create a deep copy of the dataset at the given index
const updatedDataset = {
...datasets[index], // Copy the dataset object
data: [...newData.data], // Deep copy the data array
};
// Update the dataset at the given index
datasets[index] = updatedDataset;
// Create a new object for all attributes (to force re-render)
setAttributes({
...attributes, // Copy the existing attributes
datasets: datasets, // Replace the datasets with the updated one
});
console.log("Datasets after update:", JSON.stringify(datasets, null, 2));
} else {
console.error("Dataset not found at index:", index);
}
} else {
console.error("Invalid data format in newData:", newData);
}
};
useEffect(() => {
// If needed, you can add logic to do something when datasets change
console.log('Datasets have been updated:', attributes.datasets);
}, [attributes.datasets]);
return (
<>
<Button
variant="secondary"
onClick={() => setIsOpen(true)}
style={{ marginBottom: '10px', width: '100%' }}
>
{__('Edit Dataset', 'lcp-visualize')}
</Button>
{isOpen && (
<Modal
onRequestClose={() => setIsOpen(false)}
title={__('Dataset Builder', 'lcp-visualize')}
style={{ width: '90vw', height: '90vh' }}
>
<div style={{ height: 'calc(90vh - 40px)', padding: '20px' }}>
{/* Tabs */}
<div style={{ display: 'flex', marginBottom: '20px' }}>
{attributes.datasets.map((dataset, index) => (
<button
key={index}
onClick={() => setActiveTab(index)} // Set the active tab
style={{
padding: '10px 20px',
margin: '0 5px',
backgroundColor: activeTab === index ? '#007cba' : '#f1f1f1',
color: activeTab === index ? 'white' : 'black',
border: '1px solid #ccc',
borderRadius: '5px',
cursor: 'pointer',
transition: 'background-color 0.3s ease',
}}
>
{dataset.name}
</button>
))}
</div>
{/* Render all the LCPDataGrid components */}
<div style={{ display: 'flex', flexDirection: 'column' }}>
{attributes.datasets.map((dataset, index) => (
<div
key={index}
style={{
display: activeTab === index ? 'block' : 'none', // Show only the active tab
transition: 'display 0.3s ease',
}}
>
{/* Pass key prop to trigger re-render if necessary */}
<LCPDataGrid
key={index} // Ensures each grid instance is unique
dataset={dataset.data} // Pass the specific dataset data
index={index}
updateDataset={handleUpdateDataset} // Function to handle updates
/>
</div>
))}
</div>
</div>
</Modal>
)}
</>
);
};
export default LCPDatasetBuilder;
The LCPDataGrid.js component passes back the updated data as well as the index (for the respective data object to update). I’m having problems getting the changes to actually be saved in this part of the code
const handleUpdateDataset = (index, newData) => {
console.log("newData (formatted for copy-pasting):", JSON.stringify(newData, null, 2));
// Check if newData.data is an array
if (Array.isArray(newData.data)) {
// Create a deep copy of the datasets to avoid mutating the state directly
const datasets = [...attributes.datasets];
// Check if the dataset at the specified index exists
if (datasets[index]) {
// Create a deep copy of the dataset at the given index
const updatedDataset = {
...datasets[index], // Copy the dataset object
data: [...newData.data], // Deep copy the data array
};
// Update the dataset at the given index
datasets[index] = updatedDataset;
// Create a new object for all attributes (to force re-render)
setAttributes({
...attributes, // Copy the existing attributes
datasets: datasets, // Replace the datasets with the updated one
});
console.log("Datasets after update:", JSON.stringify(datasets, null, 2));
} else {
console.error("Dataset not found at index:", index);
}
} else {
console.error("Invalid data format in newData:", newData);
}
};
I suspect it’s because React doesn’t recognize this as an actual change in the data…
If I replace
setAttributes({
...attributes, // Copy the existing attributes
datasets: datasets, // Replace the datasets with the updated one
});
with a literal datasets array like
setAttributes({
...attributes, // Copy the existing attributes
datasets: [{...new data }], // Replace the datasets with the updated one
});
then it updates as expected.