On every render, KronoGraph must decide whether any of its props have changed and what to update accordingly. KronoGraph must decide if any data has changed every time the API is called, and make the respective updates. The timeline assumes that whenever a new object is passed into a prop, property, something has changed and KronoGraph will redraw. If the same object is passed into a prop, set(), it assumes nothing has changed, even if the object itself has been altered.
By relying on referential equality to detect changes, KronoGraph can quickly and efficiently respond to re-renders, even in very large datasets.
This means that applications must be disciplined in how state is updated to prevent unnecessary redraws being called. For example, passing a new object with identical property values into the entities prop property will trigger the data to be reloaded and redrawn.
This holds true across the state tree: if a sub property has changed, a whole new object hierarchy must be created and passed into the relevant prop. property. Consider the following object in the entities prop: property:
const entities = {
'smith-j' : {
label: 'John Smith',
fontIcon: {
text: '☺',
},
},
}; To change John Smith's fontIcon.text property, we can't simply mutate the first object and pass the same into the entities prop, property, as the change will not be recognized and our timeline will stay the same.
To correctly render this change, we have to create new objects for entities, smith-j (i.e. entities['smith-j']), and fontIcon. We also need to create a new parent object to pass to set(). In the object below, we've starred the properties that need to be re-created:
const entities* = {
'smith-j'* : {
label: 'John Smith',
fontIcon*: {
text: '☺',
},
},
}; In a real app, we would have something like:
// Clone the entities object before making any changes
const newEntities = Object.assign({}, this.state.entities);
// Clone and update the entity we want to change
const newEntity = Object.assign({}, newEntities['smith-j'], {
fontIcon: {
...newEntities['smith-j'].fontIcon,
text: '$',
}
});
// Write the change back to the newEntities object
newEntities['smith-j'] = newEntity;
// Update app state and trigger a re-render
this.setState({ entities: newEntities }); // In this example, we assume that the app has a 'let currentData'
// variable containing an object with properties
// 'entities', 'events', 'entityTypes' and 'eventTypes'.
// Clone the entities object before making any changes
const newEntities = Object.assign({}, currentData.entities);
// Clone and update the entity we want to change
const newEntity = Object.assign({}, newEntities['smith-j'], {
fontIcon: {
...newEntities['smith-j'].fontIcon,
text: '$',
}
});
// Write the change back to newEntities
newEntities['smith-j'] = newEntity;
// Update the app's currentData object with the changed
// entities object and pass the new object to the timeline.
currentData = { ...currentData, entities: newEntities }
timeline.set(currentData); For an example of passing state changes in and out of the Timeline component, see the Change Event story.
Using immutability in your app's code provides better performance, fewer unexpected side-effects, and allows you to rewind or undo user actions.
There are many code patterns and functions designed for immutability, including:
Object.assign()- The spread operator
const events = {...this.state.events};const events = { ...state.events }; - Array operators like
mapandfilter - Utility libraries like lodash
We use these patterns throughout our stories.