Migration from 1.x to 2.x
⚠️ DEPRECATION NOTICE
Section titled “⚠️ DEPRECATION NOTICE”@gridsheet/react-right-menu is deprecated and will no longer be maintained.
- Do not use this package for new projects.
- Please consider using the core package (
@gridsheet/react-core) or other alternatives for right-click menu functionality.
Migration Guide: 1.x to 2.x
Section titled “Migration Guide: 1.x to 2.x”Complete guide for upgrading your GridSheet implementation from version 1.x to 2.x.
Overview
Section titled “Overview”Version 2.x introduces significant improvements and breaking changes. This guide will help you migrate your existing code safely.
Breaking Changes
Section titled “Breaking Changes”1. TableRef and StoreRef Replaced with Connector
Section titled “1. TableRef and StoreRef Replaced with Connector”Before (v1.x):
import { useTableRef, useStoreRef } from '@gridsheet/react-core';
const tableRef = useTableRef();const storeRef = useStoreRef();
<GridSheet tableRef={tableRef} storeRef={storeRef} initialCells={initialCells} />
// Access tableconst { table, apply } = tableRef.current;// Access storeconst { store, apply } = storeRef.current;After (v2.x):
import { useConnector } from '@gridsheet/react-core';
const connector = useConnector();
<GridSheet connector={connector} initialCells={initialCells} />
// Access table managerconst { tableManager } = connector.current;const { table, apply } = tableManager;
// Access store managerconst { storeManager } = connector.current;const { store, apply } = storeManager;2. Hook Unification
Section titled “2. Hook Unification”The separate useTableRef() and useStoreRef() hooks have been unified into a single useConnector() hook that provides access to both table and store managers.
Before (1.x):
import { useTableRef, useStoreRef } from '@gridsheet/react-core';
const tableRef = useTableRef();const storeRef = useStoreRef();After (2.x):
import { useConnector } from '@gridsheet/react-core';
const connector = useConnector();
// Access table managerconst { tableManager } = connector.current;const { table, apply } = tableManager;
// Access store managerconst { storeManager } = connector.current;const { store, apply } = storeManager;Hook and Function Renaming
Section titled “Hook and Function Renaming”useTableRef()→useConnector()useStoreRef()→useConnector()(same hook for both)createTableRef()→createConnector()createStoreRef()→createConnector()(same function for both)
1. Renderer Method Argument Types
Section titled “1. Renderer Method Argument Types”Renderer methods now use typed props objects instead of individual parameters.
Before (1.x):
const ScoreColorRendererMixin: RendererMixinType = { number(value: number) { if (value < 60) { return { style: { backgroundColor: "#ffcccc" } }; } else if (value < 80) { return { style: { backgroundColor: "#ffffcc" } }; } else { return { style: { backgroundColor: "#ccffcc" } }; } }}After (2.x):
const ScoreColorRendererMixin: RendererMixinType = { number({ value }: RenderProps<number>) { if (value < 60) { return { style: { backgroundColor: "#ffcccc" } }; } else if (value < 80) { return { style: { backgroundColor: "#ffffcc" } }; } else { return { style: { backgroundColor: "#ccffcc" } }; } }}2. Utility Function Renaming
Section titled “2. Utility Function Renaming”Several utility functions have been renamed for consistency.
Before (1.x):
import { constructInitialCells, constructInitialCellsOrigin } from "@gridsheet/react-core";After (2.x):
import { buildInitialCells, buildInitialCellsFromOrigin } from "@gridsheet/react-core";3. Header Configuration Migration
Section titled “3. Header Configuration Migration”headerHeight and headerWidth options have been moved from global Options to Table-specific settings.
Before (1.x):
<GridSheet options={{ headerHeight: 100, headerWidth: 100, }}/>After (2.x):
<GridSheet initialCells={buildInitialCells({ cells: { 0: { width: 100, // Set header width height: 100, // Set header height }, } })}/>And if you need to set header height and width for the table, you can do it like this:
table.setHeaderHeight(100);table.setHeaderWidth(100);4. Table Method Name Changes
Section titled “4. Table Method Name Changes”Several table methods have been renamed for better clarity and consistency.
Before (1.x):
// Data access methodstable.getMatrixFlatten(args?: GetFlattenPropsWithArea): any[][];table.getObjectFlatten(args?: GetFlattenProps): CellsByAddressType;table.getRowsFlatten(args?: GetFlattenProps): CellsByAddressType[];table.getColsFlatten(args?: GetFlattenProps): CellsByAddressType[];
// Row and column operationstable.addRowsAndUpdate(args: { y: number; numRows: number; baseY: number; diff: CellsByAddressType; partial?: boolean; updateChangedAt?: boolean; reflection?: StoreReflectionType }): UserTable;table.addRows(args: { y: number; numRows: number; baseY: number; reflection?: StoreReflectionType }): UserTable;table.deleteRows(args: { y: number; numRows: number; reflection?: StoreReflectionType }): UserTable;table.addColsAndUpdate(args: { x: number; numCols: number; baseX: number; diff: CellsByAddressType; partial?: boolean; updateChangedAt?: boolean; reflection?: StoreReflectionType }): UserTable;table.addCols(args: { x: number; numCols: number; baseX: number; reflection?: StoreReflectionType }): UserTable;table.deleteCols(args: { x: number; numCols: number; reflection?: StoreReflectionType }): UserTable;After (2.x):
// Data access methodstable.getFieldMatrix(args?: GetFieldPropsWithArea): any[][];table.getFieldObject(args?: GetFieldProps): { [address: Address]: any };table.getFieldRows(args?: GetFieldProps): { [address: Address]: any }[];table.getFieldCols(args?: GetFieldProps): { [address: Address]: any }[];
// Row and column operations (merged functionality)table.insertRows(args: { y: number; numRows: number; baseY: number; diff?: CellsByAddressType; partial?: boolean; updateChangedAt?: boolean; reflection?: StorePatchType }): UserTable;table.removeRows(args: { y: number; numRows: number; reflection?: StorePatchType }): UserTable;table.insertCols(args: { x: number; numCols: number; baseX: number; diff?: CellsByAddressType; partial?: boolean; updateChangedAt?: boolean; reflection?: StorePatchType }): UserTable;table.removeCols(args: { x: number; numCols: number; reflection?: StorePatchType }): UserTable;5. GetProps Type Changes
Section titled “5. GetProps Type Changes”The GetProps type has been updated to use a more explicit refEvaluation property instead of the boolean evaluates property.
Before (1.x):
type GetProps = { // null for the system, do not use it evaluates?: boolean | null; raise?: boolean; filter?: CellFilter;};After (2.x):
type GetProps = { // do not use 'SYSTEM', it is reserved for internal use. refEvaluation?: RefEvaluation; raise?: boolean; filter?: CellFilter;};Usage Examples:
// Before (1.x)table.getCell({ y: 1, x: 1 }, { evaluates: true });table.getCell({ y: 1, x: 1 }, { evaluates: false });table.getCell({ y: 1, x: 1 }, { evaluates: null });
// After (2.x)table.getCell({ y: 1, x: 1 }, { refEvaluation: 'COMPLETE' });table.getCell({ y: 1, x: 1 }, { refEvaluation: 'RAW' });table.getCell({ y: 1, x: 1 }, { refEvaluation: 'SYSTEM' });RefEvaluation Values:
'COMPLETE'- Evaluates formulas and returns the final value (equivalent toevaluates: true)'RAW'- Returns the raw cell value without formula evaluation (equivalent toevaluates: false)'SYSTEM'- Reserved for internal use (equivalent toevaluates: null)
6. Event Handlers Moved from Options to Hub
Section titled “6. Event Handlers Moved from Options to Hub”Event handlers have been moved from the options prop to the hub configuration for better organization and consistency.
Before (1.x):
<GridSheet options={{ onChange: (table, positions) => { console.log('Data changed:', positions); }, onSave: (table, positions) => { console.log('Data saved:', positions); }, onSelect: (table, positions) => { console.log('Selection changed:', positions); }, }} initialCells={initialCells}/>After (2.x):
const hub = useHub({ onChange: ({ table, points }) => { console.log('Data changed:', points); }, onSave: ({ table, points }) => { console.log('Data saved:', points); }, onSelect: ({ table, points }) => { console.log('Selection changed:', points); }, onRemoveRows: ({ table, ys }) => { console.log('Rows removed:', ys); }, onRemoveCols: ({ table, xs }) => { console.log('Columns removed:', xs); }, onInsertRows: ({ table, y, numRows }) => { console.log('Rows inserted at position', y, 'count:', numRows); }, onInsertCols: ({ table, x, numCols }) => { console.log('Columns inserted at position', x, 'count:', numCols); }, onKeyUp: ({ e, points }) => { console.log('Key pressed:', e.key, 'at position:', points); }, onInit: ({ table }) => { console.log('Table initialized:', table.sheetName); },});
<GridSheet hub={hub} initialCells={initialCells} />New Event Handlers in 2.x:
onRemoveRows: Called when rows are removed from the spreadsheetonRemoveCols: Called when columns are removed from the spreadsheetonInsertRows: Called when rows are inserted into the spreadsheetonInsertCols: Called when columns are inserted into the spreadsheetonKeyUp: Called when a key is pressed in the cell editoronInit: Called when the table is initialized
Event Handler Table Identification
Section titled “Event Handler Table Identification”When multiple sheets share the same hub, event handlers receive a table parameter that contains the UserTable instance. You can use table.sheetName to identify which sheet triggered the event:
const hub = useHub({ onChange: ({ table, points }) => { // Use table.sheetName to identify the source sheet if (table.sheetName === 'Sales') { console.log('Sales sheet data changed:', points); } else if (table.sheetName === 'Inventory') { console.log('Inventory sheet data changed:', points); } }, onRemoveRows: ({ table, ys }) => { console.log(`Rows removed from ${table.sheetName}:`, ys); }, onInsertCols: ({ table, x, numCols }) => { console.log(`Inserted ${numCols} columns at position ${x} in ${table.sheetName}`); }, onKeyUp: ({ e, points }) => { console.log(`Key pressed: ${e.key} at position:`, points); }, onInit: ({ table }) => { console.log(`Table initialized: ${table.sheetName}`); },});Important: Always use table.sheetName for conditional logic when multiple sheets share the same hub to ensure proper event handling and data management.
7. Connector Function Renaming: dispatch → sync
Section titled “7. Connector Function Renaming: dispatch → sync”The function name in the connector has been renamed from dispatch to sync to avoid confusion with Redux dispatch and better reflect its purpose.
Before (1.x):
import { useTableRef } from '@gridsheet/react-core';
function MyComponent() { const tableRef = useTableRef();
const handleUpdate = () => { if (tableRef.current) { const { table, dispatch } = tableRef.current; const updatedTable = table.update({ diff: { 'A1': { value: 'Updated' } } }); dispatch(updatedTable); } };}After (2.x):
import { useConnector } from '@gridsheet/react-core';
function MyComponent() { const connector = useConnector();
const handleUpdate = () => { const { tableManager } = connector.current; if (tableManager) { const { table, apply } = tableManager; table.update({ diff: { 'A1': { value: 'Updated' } } }); apply(table); } };}Type Definition:
// Before (1.x)type TableRef = { table: UserTable; dispatch: (table: UserTable) => void;};
// After (2.x)type Connector = { tableManager: { instance: UserTable; sync: (table: UserTable) => void; }; storeManager: { instance: StoreType; sync: Dispatcher; };};Complete Migration Example
Section titled “Complete Migration Example”Before (1.x)
Section titled “Before (1.x)”import * as React from "react";import { GridSheet, constructInitialCells, BaseFunction, prevention, SheetProvider,} from "@gridsheet/react-core";
const ScoreColorRendererMixin: RendererMixinType = { number(value: number) { if (value < 60) { return { style: { backgroundColor: "#ffcccc" } }; } else if (value < 80) { return { style: { backgroundColor: "#ffffcc" } }; } else { return { style: { backgroundColor: "#ccffcc" } }; } }}
function App() { return ( <SheetProvider> <GridSheet sheetName={'criteria'} initialCells={constructInitialCells({ matrices: { A1: [ [0, "=A1+60", "=B1+10", "=C1+10", "=D1+10", "=E1+5"], ["E🤯", "D🥺", "C😒", "B😚", "A🥰", "S😇"], ], }, cells: { 1: { style: { backgroundColor: '#ddd' } }, }, })} options={{ headerHeight: 100, headerWidth: 100, }} /> <GridSheet sheetName={'grades'} initialCells={constructInitialCells({ matrices: { A1: [ ["Name", "Score", "Rank", "Comment"], [ "apple", 50, "=HLOOKUP(B2, criteria!$A$1:$F$2, 2, true)", "Pie", ], ["orange", 82, "=HLOOKUP(B3, criteria!$A$1:$F$2, 2, true)"], ["grape", 75, "=HLOOKUP(B4, criteria!$A$1:$F$2, 2, true)"], ["melon", 98, "=HLOOKUP(B5, criteria!$A$1:$F$2, 2, true)"], ["banana", 65, "=HLOOKUP(B6, criteria!$A$1:$F$2, 2, true)"], ], }, cells: { B: { renderer: ScoreColorRendererMixin, } } })} /> </SheetProvider> );}After (2.x)
Section titled “After (2.x)”import * as React from "react";import { GridSheet, buildInitialCells, useHub, Renderer, RendererMixinType, RenderProps, makeBorder,} from "@gridsheet/react-core";
// Updated renderer with new method signatureconst ScoreColorRendererMixin: RendererMixinType = { number({ value }: RenderProps<number>) { if (value < 60) { return { style: { backgroundColor: "#ffcccc" } }; } else if (value < 80) { return { style: { backgroundColor: "#ffffcc" } }; } else { return { style: { backgroundColor: "#ccffcc" } }; } }}
function App() { const hub = useHub({ renderers: { scoreColor: new Renderer({ mixins: [ScoreColorRendererMixin] }), }, });
return ( <> <GridSheet hub={hub} sheetName={'criteria'} initialCells={buildInitialCells({ matrices: { A1: [ [0, "=A1+60", "=B1+10", "=C1+10", "=D1+10", "=E1+5"], ["E🤯", "D🥺", "C😒", "B😚", "A🥰", "S😇"], ], }, cells: { 1: { style: { backgroundColor: '#ddd', ...makeBorder({ all: '1px solid #ccc' }) } }, }, })} /> <GridSheet hub={hub} sheetName={'grades'} initialCells={buildInitialCells({ matrices: { A1: [ ["Name", "Score", "Rank", "Comment"], [ "apple", 50, "=HLOOKUP(B2, criteria!$A$1:$F$2, 2, true)", "Pie", ], ["orange", 82, "=HLOOKUP(B3, criteria!$A$1:$F$2, 2, true)"], ["grape", 75, "=HLOOKUP(B4, criteria!$A$1:$F$2, 2, true)"], ["melon", 98, "=HLOOKUP(B5, criteria!$A$1:$F$2, 2, true)"], ["banana", 65, "=HLOOKUP(B6, criteria!$A$1:$F$2, 2, true)"], ], }, cells: { B: { renderer: 'scoreColor', } } })} /> </> );}Migration Steps
Section titled “Migration Steps”Step 1: Update Imports
Section titled “Step 1: Update Imports”// Old importsimport { constructInitialCells, SheetProvider, useTableRef, useStoreRef } from '@gridsheet/react-core';
// New importsimport { buildInitialCells, useHub, Renderer, makeBorder, useConnector } from '@gridsheet/react-core';Step 2: Update Connector Usage
Section titled “Step 2: Update Connector Usage”Replace useTableRef and useStoreRef with useConnector:
// Before (1.x)const tableRef = useTableRef();const storeRef = useStoreRef();
// After (2.x)const connector = useConnector();
// Access table managerconst { tableManager } = connector.current;const { table, apply } = tableManager;
// Access store managerconst { storeManager } = connector.current;const { store, apply } = storeManager;Step 3: Migrate Renderers
Section titled “Step 3: Migrate Renderers”Update all renderer mixins to use the new method signature with RenderProps:
// Beforeconst MyRenderer = { string(value: string) { /* ... */ }}
// Afterconst MyRenderer: RendererMixinType = { string({ value }: RenderProps<string>) { /* ... */ }}Step 4: Update Hub Configuration
Section titled “Step 4: Update Hub Configuration”const hub = useHub({ renderers: { myRenderer: new Renderer({ mixins: [MyRenderer] }), }, // Move event handlers from options to hub onChange: ({ table, points }) => { console.log('Data changed:', points); }, onSave: ({ table, points }) => { console.log('Data saved:', points); }, onSelect: ({ table, points }) => { console.log('Selection changed:', points); }, onRemoveRows: ({ table, ys }) => { console.log('Rows removed:', ys); }, onRemoveCols: ({ table, xs }) => { console.log('Columns removed:', xs); }, onInsertRows: ({ table, y, numRows }) => { console.log('Rows inserted at position', y, 'count:', numRows); }, onInsertCols: ({ table, x, numCols }) => { console.log('Columns inserted at position', x, 'count:', numCols); }, onKeyUp: ({ e, points }) => { console.log('Key pressed:', e.key, 'at position:', points); }, onInit: (table) => { console.log('Table initialized:', table.sheetName); },});Step 5: Move Event Handlers to Hub
Section titled “Step 5: Move Event Handlers to Hub”Move all event handlers from the options prop to the hub configuration:
// Before (1.x)<GridSheet options={{ onChange: (table, positions) => { /* ... */ }, onSave: (table, positions) => { /* ... */ }, onSelect: (table, positions) => { /* ... */ }, }} initialCells={initialCells}/>
// After (2.x)const hub = useHub({ onChange: ({ table, points }) => { /* ... */ }, onSave: ({ table, points }) => { /* ... */ }, onSelect: ({ table, points }) => { /* ... */ }, // New event handlers available in 2.x onRemoveRows: ({ table, ys }) => { /* ... */ }, onRemoveCols: ({ table, xs }) => { /* ... */ }, onInsertRows: ({ table, y, numRows }) => { /* ... */ }, onInsertCols: ({ table, x, numCols }) => { /* ... */ }, onKeyUp: ({ e, points }) => { /* ... */ }, onInit: ({ table }) => { /* ... */ },});
<GridSheet hub={hub} initialCells={initialCells} />Step 6: Remove Header Options
Section titled “Step 6: Remove Header Options”Remove headerHeight and headerWidth from options and set them on the table if needed.
Step 6: Test Functionality
Section titled “Step 6: Test Functionality”- Test all features to ensure they work as expected
- Check TypeScript errors and fix type issues
- Verify performance improvements
- Test edge cases and error handling
Step 7: Update Table Method Calls
Section titled “Step 7: Update Table Method Calls”Update any direct table method calls to use the new method names:
// Before (1.x)const matrix = table.getMatrixFlatten({ area: { top: 0, left: 0, bottom: 2, right: 2 } });const cells = table.getObjectFlatten({ evaluates: true });const rows = table.getRowsFlatten();const cols = table.getColsFlatten();
table.addRows({ y: 2, numRows: 1, baseY: 2 });table.deleteRows({ y: 2, numRows: 1 });table.addCols({ x: 2, numCols: 1, baseX: 2 });table.deleteCols({ x: 2, numCols: 1 });
// After (2.x)const matrix = table.getFieldMatrix({ area: { top: 0, left: 0, bottom: 2, right: 2 } });const cells = table.getFieldObject({ refEvaluation: 'COMPLETE' });const rows = table.getFieldRows();const cols = table.getFieldCols();
table.insertRows({ y: 2, numRows: 1, baseY: 2 });table.removeRows({ y: 2, numRows: 1 });table.insertCols({ x: 2, numCols: 1, baseX: 2 });table.removeCols({ x: 2, numCols: 1 });Note: The addRowsAndUpdate and addColsAndUpdate methods have been merged into insertRows and insertCols respectively. Use the diff parameter to update cells after insertion:
// Before (1.x)table.addRowsAndUpdate({ y: 2, numRows: 1, baseY: 2, diff: { 'A2': { value: 'New Row' } }});
// After (2.x)table.insertRows({ y: 2, numRows: 1, baseY: 2, diff: { 'A2': { value: 'New Row' } }});Step 8: Update GetProps Usage
Section titled “Step 8: Update GetProps Usage”Update any usage of the GetProps type to use the new refEvaluation property:
// Before (1.x)table.getCell({ y: 1, x: 1 }, { evaluates: true });table.getCell({ y: 1, x: 1 }, { evaluates: false });
// After (2.x)table.getCell({ y: 1, x: 1 }, { refEvaluation: 'COMPLETE' });table.getCell({ y: 1, x: 1 }, { refEvaluation: 'RAW' });Step 10: Update Connector Function Calls
Section titled “Step 10: Update Connector Function Calls”Update connector usage to use sync instead of dispatch:
// Before (1.x)if (tableRef.current) { const { table, dispatch } = tableRef.current; const updatedTable = table.update({ diff: { 'A1': { value: 'New' } } }); dispatch(updatedTable);}
// After (2.x)const { tableManager } = connector.current;if (tableManager) { const { table, apply } = tableManager; table.update({ diff: { 'A1': { value: 'New' } } }); apply(table);}