Skip to main content

Updates and mutations

The tree is immutable. Every mutation — drag-to-resize, dropping a panel, clicking the remove button — produces a brand-new tree and hands it to onChange. The library never mutates the value you pass in.

That immutability is made practical by a small set of helpers that take a tree and a path, and return a new tree.

updateTree and MosaicUpdate​

Everything funnels through updateTree:

import { updateTree, MosaicUpdate } from 'react-mosaic-component';

const updates: MosaicUpdate<string>[] = [
// flip the root split from row to column
{ path: [], spec: { direction: { $set: 'column' } } },
];
const next = updateTree(tree, updates);

A MosaicUpdate is a { path, spec } pair. The spec is an immutability-helper command — $set, $push, $apply, etc. — applied at the node addressed by path.

You rarely write specs by hand. The library ships a handful of factories that produce the common ones for you.

The update factories​

FactoryWhat it does
createRemoveUpdate(tree, path)Removes the node at path, collapsing its parent if needed.
createHideUpdate(tree, path)Temporarily hides the node at path (sets its split percentage to 0).
createExpandUpdate(path, percentage)Grows the node at path to take percentage of its parent, at every level up to the root.
createDragToUpdates(tree, sourcePath, destinationPath, dropInfo)Produces the full update sequence for a drag-and-drop operation. dropInfo is e.g. { type: 'split', position: 'left' }.

createDragToUpdates returns an array of updates; the others return a single MosaicUpdate<T>. Put them together in one array and pass it to updateTree:

const next = updateTree(tree, [createRemoveUpdate(tree, [1, 0])]);

Example: a "reset layout" button​

import { useState } from 'react';
import {
Mosaic,
MosaicWindow,
MosaicNode,
createBalancedTreeFromLeaves,
getLeaves,
} from 'react-mosaic-component';

function App() {
const [tree, setTree] = useState<MosaicNode<string> | null>(INITIAL);

const rebalance = () => {
setTree((current) => createBalancedTreeFromLeaves(getLeaves(current)));
};

return (
<>
<button onClick={rebalance}>Auto arrange</button>
<Mosaic value={tree} onChange={setTree} renderTile={/* ... */} />
</>
);
}

getLeaves walks the tree and returns the leaf keys in order; createBalancedTreeFromLeaves packs them back into a minimal-depth n-ary tree. Chaining the two gives you "reshuffle into a clean grid" in one line.

Why not just mutate?​

Immutability keeps rendering predictable: React re-renders only when the reference changes, and because Mosaic is happy to be driven by value, you get free time-travel. Keep a stack of past trees and you have undo for the price of one array.

const [history, setHistory] = useState<MosaicNode<string>[]>([INITIAL]);
const tree = history[history.length - 1];

const push = (next: MosaicNode<string>) =>
setHistory((h) => [...h, next]);

const undo = () =>
setHistory((h) => (h.length > 1 ? h.slice(0, -1) : h));

Because mosaic hands you a whole new tree on every onChange, this pattern is as simple as it looks.