There are several components that all manage data, such as, Table
, ResourceList
, StructuredList
, and TileCatalog
. Each of these have very different interfaces for how data is provided and how content is rendered. There needs to be a closer discussion around each of these components on how they manage and render items.
Table
and StructuredList
share a similar data model for columns and data, although the data model is not ideal for consumption, since it forces consumers to transform their data into model for the table. Table and StructuredList are very similar in that a basic table is rendered like a StructuredList, so, it begs the question as to why StructuredList exists? (do we really need both?)
ResourceList
uses a normal data model of a json array of flat objects, but, lacks any ability to customize what is in the list.
TileCatalog
uses a data model that is a mix of data and React content nodes, that makes it problematic for consuming, since, I have to pre-build all my tiles before passing it to the component.
Every component that manages a list
of data, should use the same interface for the data. I should be able to set my data items on a Table
or TileCatalog
and not be expected to transform my data differently for each of those.
When we have repeated data, we can always think about that a json array of rows, where each row is a json object. Each row will usually have an id field, but that field will likely be different depending on the data source. ie, sometimes the id
might be id
, or _id
, or uniqueId
, rowstamp
, etc.
When we have components that need to render repeatable data, we should always try to defer that rendering as late as possible, and not make the consumer pre-build the content as part of the data. Using the the React Render Props Pattern is generally a good way defer the rendering a repeated component, and give the flexibility to the consumer of how render. It also ensure that you can ensure that you are not mixing data and view components in the same structure.
As a general rule, if your repeatable data structure requires that that a consumer start iterating their data and transform it, radically, adding in View
nodes, alarm bells should be going off, as a bad design.
Ideally, as noted, we should be able to move our data between components and not have to transform the data over and over. At a minimum, the interface to a repeating component that manages data should be something like...
const data = [
{_id: 0, name: 'bill', age: 39},
{_id: 1, name: 'tom', age: 42}
];
const renderFunc = (props) => {
return <div>{props.item.name}</div>
};
<List idAttribute="_id" data={data} itemRenderer={renderFunc}/>
Typically the render function will want to accept a props
structure where the props
will have the item
as the very minium piece of information, but you might also want to pass the id
, index
and even the list of the items
. Components can provide a default render function if they don't wan the user to have to specify one, every time.
As a consumer, I should be able to pass my data to a Table or TileCatalog. I might need to provide different kinds of renders for each (table using a cell render and tile catalog using a tile renderer) but the data interface remains the same.
Some components may actually manage their own "selection" state, and as such, components that manage selection, should also be able to set their initial selection state based on the data, such as...
<List idAttribute="_id" data={data} itemRenderer={renderFunc} selectedAttribute="_selected_" onRowSelected={(row, selectionStatus)=>updateSelectionInModel(row, selectionStatus)}/>
The use case for this would something like this.. Let's say I have Table View but I allow the user to switch to a Tile View. The data manager will manage the selection status, since, if I select a row in the table and then move to a tile view, that row should still be selected. If select additional tiles, and then switch back to the table view, then those rows should be selected in the table as well.