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>[] = [
{ path: ['direction' /* path index */], spec: { $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(path)Temporarily hides a split node (sets its percentage to 0).
createExpandUpdate(path, percentage)Grows the node at path to take percentage of its parent.
createDragToUpdates(tree, sourcePath, destinationPath, position)Produces the full update sequence for a drag-and-drop operation.

All of them return MosaicUpdate<T>[], so you compose them by concatenating arrays and passing the result to updateTree.

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.