is a React.js component for displaying and editing data in a spreadsheet-like way. This guide shows you how to integrate it with the well-known pure Javascript library — . ReactGrid Chart.js Why ReactGrid? There are plenty of different data tables available on the Internet, which perform great if you want to display one object per row. Each of these objects has to have exactly the same static properties, which are mapped to columns in the table. was designed to be independent of your data model. It doesn’t care about your schema. You can render anything in any cell and thus you are able to display things the way you like it. ReactGrid Unlike other grid components, also performs great on mobile devices or those with a touch capability and provides the same experience as on a desktop. ReactGrid Before we get started let’s list three main tasks: displaying the collected data will be achieved with . To be we will re-render the view only when the source data has changed. In this example, raw data comes from an audiometer - a device that is used for making hearing tests. In a nutshell, audiometer measures multiple hearing difficulties at many frequencies, and the audiogram is a way of visualizing such disorders, ReactGrid reactive visualize the collected data on the line chart using and its React wrapper, Chart.js add a possibility to enter a new value, and re-render the whole view with an updated state. Let’s code! Initialize the project Nothing simpler — just type one of the commands below into your terminal to initiate a React app with Typescript support. ‘Create React App’ will take care of all the necessary stuff. $ $ npx create-react-app reactgrid-chartjs- audiogram --template typescript # or yarn create react-app reactgrid-chartjs- audiogram --template typescript Define useful interfaces and types First, we need to declare a few interfaces and types that help us to keep everything in the right place and order. In this particular example, we know all about the data that we want to process. A good idea is to ‘be as narrow’ as possible. ColumnMap { line: ; : ; : ; : ; : ; : ; : ; : ; : ; : ; } ColumnKey = keyof ColumnMap; Freq = ColumnMap[ColumnKey]; RowsMap { hearing_aid: ; blue: ; aid_higher: ; aid_lower: ; dotted_line_higher: ; dotted_line_lower: ; pattern_line: ; } RowKey = keyof RowsMap; AudiogramProps { color?: ; title?: ; } FreqSample = { freq: Freq; value: ; }; Data = { [P keyof RowsMap]: FreqSample[]; }; /** * ReactGrid related interfaces - columns mapping */ export interface "Line" "0" "0" "125" "125" "250" "250" "500" "500" "1000" "1k" "2000" "2k" "4000" "4k" "8000" "8k" "16000" "16k" /** * Getting all object keys of ColumnMap interface, * e.g. "line" | "0" | "125" | "250" | ... */ export type /** * An union of all possible columns values, * e.g. "0" | "125" | ... | "8k" | "16k" */ export type /** * Interface defines all of the rows that contais array of samples * with coresponding title */ export interface "Hearing aid (device)" "Blue Line" "Hearing aid higher" "Hearing aid lower" "Dotted line higher" "Dotted line lower" "Pattern line" /** * An union of RowsMap kinterface keys, * e.g. "hearing_aid" | "blue" | "aid_higher" */ export type /** * Main component props */ export interface string string /** * Single frequency sample * * @example * const sample = { freq: '4k', value: 14 }; */ export type number /** * Object that defines all the data collecte during * the course of the test */ export type in Mark the columns and rows Relying on those interfaces now we can introduce function. getColumns In our app, we got a column, and after that, we got columns which are related to a particular frequency from 0Hz to 16000Hz. Line columnMap: ColumnMap = { line: , : , : , : , : , : , : , : , : , : }; getColumns = (): Column[] => [ { columnId: , width: }, { columnId: , width: }, { columnId: , width: }, { columnId: , width: }, { columnId: , width: }, { columnId: , width: }, { columnId: , width: }, { columnId: , width: }, { columnId: , width: }, { columnId: , width: } ]; /** * Object that contains all possible columns including * the `line` (header of e.g. "Blue Line") and * all possible frequrencies as object keys and their labels */ export const "Line" 0 "0" 125 "125" 250 "250" 500 "500" 1000 "1k" 2000 "2k" 4000 "4k" 8000 "8k" 16000 "16k" /** * Function returns an array of ReactGrid compatible columns, * `columnId` coresponds to keyd of an object defined above */ export const "Line" 160 0 70 125 70 250 70 500 70 1000 70 2000 70 4000 70 8000 70 16000 70 The next stage is mapping all the rows. We make it in a similar way to previous examples. rowsMap: RowsMap = { hearing_aid: , blue: , aid_higher: , aid_lower: , dotted_line_higher: , dotted_line_lower: , pattern_line: }; /** * Object that contain all possible test results during * the course of the test */ export const "Hearing aid (device)" "Blue Line" "Hearing aid higher" "Hearing aid lower" "Dotted line higher" "Dotted line lower" "Pattern line" Define the data As we defined our data, it’s time to define our data that we are working on. function returns an object whose each key must exist within the interface. Each key of this object contains an array of objects. getData RowsMap Freq getData = (): { { hearing_aid: [ { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: } ], blue: [ { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: }, { freq: columnMap[ ], value: } ], }; }; export const => Data return "0" NaN "125" 0 "250" 0 "500" 2 "1000" 10 "2000" 17 "4000" 26 "8000" 21 "16000" NaN "0" 10 "125" 12 "250" 17 "500" 23 "1000" 35 "2000" 43 "4000" 39 "8000" 45 "16000" 7 // ... Map the data to ReactGrid Now we are ready to generate rows that directly feed into . ReactGrid Each row contains the same amount of cells, but all of them can be arbitrarily placed in any order. compare = freqSample.freq === columnMap[freqKey]; getRows = ( data: Data, columnsOrder: ColumnKey[] = [ , , , , , , , , , ], rowsOrder: RowKey[] = [ , , , , , , ] ): Row<NumberCell | TextCell | HeaderCell>[] => [ ...rowsOrder.map<Row<NumberCell | TextCell | HeaderCell>> const ( ) => freqSample: FreqSample, freqKey: ColumnKey export const "line" "0" "125" "250" "500" "1000" "2000" "4000" "8000" "16000" "hearing_aid" "blue" "aid_higher" "aid_lower" "dotted_line_higher" "dotted_line_lower" "pattern_line" ( ) ]; ( ) => { item = data[rowKey]; { rowId: rowKey, height: 35, cells: [ { : "text", text: rowsMap[rowKey], nonEditable: }, ...columnsOrder .filter( ) .map<NumberCell>( ) ] }; } rowKey const return type true ( ) => column !== "line" column ( ) => ( ) column { : " ", value: item.find( ).value } type number ( ) => compare( ) freqSample freqSample, column The main component — Audiogram It is time to create the main component — and wrap up already written code. As you can see we stored our data inside React hook. Audiogram useState always expects two props — (they are constant and don’t change over time) and (they are calculated every time the component is rerendered). ReactGrid columns rows Audiogram Audiogram: React.FC<AudiogramProps> = ({ title = }) => { [rawData, setRawData] = React.useState(getData()); columns = getColumns(); rows = getRows(rawData); ( <div className= style={{ width: }}> <ReactGrid rows={rows} columns={columns} /> < const "AUDIOGRAM: ReactGrid + Chart.js app" const const const return "chart" 800 /div> ); }; export default Audiogram; All that’s left is to render the component with: * React ; { render } ; Audiogram ; ; render(<Audiogram color= />, .getElementById( )); import as from "react" import from "react-dom" import from "./Audiogram" import "./styles.css" "#107C41" document "root" Apply changes with the cell editor There are two things left to do: Add a header row to mark the data (devices and all the frequencies); Add possibility to edit data with cell editor; ReactGrid’s Adding the header row To add it we have to create a short function called . As an argument, it gets a column order (as keys of columns) and returns a row object that contains only a cell of the type. We also added some green background to those cells. getHeaderRow header getHeaderRow = (columnsOrder: ColumnKey[]): Row<HeaderCell> => ({ rowId: , height: , cells: columnsOrder.map<HeaderCell>( ({ : , text: columnMap[columnKey], style: { background: columnKey !== ? : , color: , }, })), }) export const "headerRow" 50 => columnKey type "header" "line" "#1DA259" "#107C41" "white" Editing frequency values in cell editor At this moment behaves as a read-only. To change that we updated the component by adding our handler function called . We expect that only will be changed, therefore we marked the argument as . Our task is to change data on the basis has been rendered. ReactGrid Audiogram handleChanges NumberCell changes CellChange<NumberCell>[] ReactGrid Cell editor opens when it receives double-click action or the Enter key is pressed. Then you can type a new value in and then commit the change. If we we get an array of objects as shown below: console.log(changes) [ { : , : , : , : { : , : , : }, : { : , : , : } } ] "type" "number" "columnId" "250" "rowId" "aid_lower" "previousCell" "type" "number" "text" "0" "value" 0 "newCell" "type" "number" "text" "45" "value" 45 To change our raw data we have to find where the change takes place. Then loop over all frequency samples and apply a new value ( ) to an appropriate cell or just return without changes. rowId change.newCell.value Data visualization with Chart.js library delivers plenty of components to visualize data, but this time we focus on a single one — from that we can use as a React component. Chart.js Line react-chartjs-2 We have to create two functions: 1. — this function should return an object that contains two fields. The — which is an array of frequency title label and then field to provide the field which contains an array of values for each frequency. You can also style your line by setting for example a or for a better experience. getChartData labels datasets data backgroundColor pointRadius getChartData = (rawData: Data, max: = ): ({ labels: [ , , , , , , , , ], datasets: [ { label: , data: ( ).fill(max), pointRadius: , backgroundColor: , fill: }, { label: , borderColor: , fill: , data: rawData.blue.map( value), pointRadius: }, ] }); export const number 80 => ChartData "0" "125" "250" "500" "1k" "2k" "4k" "8k" "Hz" "Chart max" new Array 9 0 "#107C41" "+1" "" "#107C41" false ( ) => { value } 0 // ... 2. — here we return an object that is compatible with interface. We want to disable legend, set the title, display, and adjust axes. getChartOptions ChartOptions That’s all! The job is done, now you can check the result below. Summary What you learned after completing this guide: what is and how to do a fully functional app; ReactGrid how you can use it in a reactive way; why Typescript is also helpful in a small-scale project to avoid the most common mistakes. As you see integrating with other libraries like is not so hard. Of course, you don’t need to start a Typescript project and make all data mappings to compose a predictable solution. ReactGrid Chart.js Previously published at https://reactgrid.com/blog/how-to-integrate-reactgrid-with-chart.js/