Managing state - Controlled mode
Managing SpreadSheet state externally and syncing internal state like Undo/redo is easy using produceState
. ProduceState adds undo/redo
support. Thats the only difference.
You are free to use React setState
, if you do not require undo/redo.
Using produceState
Diffs between the current state and previous state is generated by Immer. We recommend using immer to manage state as it lets you immutability update deeply nested state easily.
import React, { useState, useRef } from 'react'
import SpreadSheet, { defaultSheets, produceState } from '@rowsncolumns/spreadsheet'
function App () {
const gridRef = useRef()
const [ sheets, setSheets ] = useState(defaultSheets)
const handleClick = () => {
setSheets(prev => {
// Undo state is managed automatically.
// This uses immer internally.
// draft is an immer Proxy
// Similar to `immer` produce, but 3rd argument is required so we can save patches
return produceState(prev, draft => {
const sheet = draft.sheets[0]
if (sheet) {
sheet.cells[1] = sheet.cells[1] ?? {}
sheet.cells[1][1] = sheet.cells[1][1] ?? {}
sheet.cells[1][1].text = 'Hello world'
}
}, gridRef);
})
}
return (
<div>
<button onClick={handleClick}>Update spreadsheet</button>
<SpreadSheet
ref={gridRef}
sheets={sheets}
onChange={setSheets}
/>
</div>
)
}
Demo
Click button to update cell. Undo will be active
fx
Sheet1
Using setState and Immer
import { produceWithPatches } from 'immer'
import React, { useState, useRef } from 'react'
import SpreadSheet, { defaultSheets } from '@rowsncolumns/spreadsheet'
function App () {
const gridRef = useRef()
const [ sheets, setSheets ] = useState(defaultSheets)
const handleClick = () => {
setSheets(prev => {
const [ newState, patches, inversePatches ] = produceWithPatches({ sheets: prev }, draft => {
const sheet = draft.sheets[0]
if (sheet) {
sheet.cells[1] = sheet.cells[1] ?? {}
sheet.cells[1][1] = sheet.cells[1][1] ?? {}
sheet.cells[1][1].text = 'Hello world'
}
});
// This updates the internal undo state
gridRef.current.addUndoPatch({ patches, inversePatches })
return newState.sheets
})
}
return (
<div>
<button onClick={handleClick}>Update spreadsheet</button>
<SpreadSheet
ref={gridRef}
sheets={sheets}
onChange={setSheets}
/>
</div>
)
}
Demo
Click button to update cell. Undo will be active
fx
Sheet1
Imperative methods
You can also update cells imperatively by accessing gridRef.current.dispatch
const App = () => {
const gridRef = useRef()
const [ sheets, setSheets ] = useState(defaultSheets)
const handleClick = () => {
gridRef.current.dispatch({
type: 'UPDATE_CELLS',
changes: {
'Sheet1': {
1: {
1: {
text: 'Hello there'
}
}
}
}
})
}
return (
<div>
<button onClick={handleClick}>Update spreadsheet</button>
<SpreadSheet
ref={gridRef}
sheets={sheets}
onChange={setSheets}
autoFocus={false}
/>
</div>
)
}
Demo
Click button to update cell. Undo will be active
fx
Sheet1