Contents
- 🚨 Breaking Changes
- The exports of @fluid-experimental/tree-react-api have been moved to the new @fluidframework/react package and placed under its /alpha exports (#25542)
- 🌳 SharedTree DDS Changes
- Added APIs for tracking observations of SharedTree content for automatic invalidation (#25459)
- Remove JsonValidator (#25381)
- Add configuredSharedTreeBeta (#25531)
- ⚠️ Deprecations
asTreeViewAlpha
has been deprecated in favor ofasAlpha
. (#25512)- Move IPersistedCache types to driver-definitions (#25518)
🚨 Breaking Changes
The exports of @fluid-experimental/tree-react-api have been moved to the new @fluidframework/react package and placed under its /alpha exports (#25542)
@fluid-experimental/tree-react-api
has been adjusted to align with Fluid Framework's API Support Levels. It has been renamed to @fluidframework/react
and all existing APIs are now available under @fluidframework/react/alpha
.
Since this package was under @fluid-experimental
, previously it implicitly made no guarantees. Now all the APIs are @alpha
, which also amounts to making no guarantees but makes it possible to promote APIs to @beta
in the future to offer some stability.
To accommodate this change, all users of this package will need to adjust:
- Package dependencies from
"@fluid-experimental/tree-react-api"
to"@fluidframework/react"
. - Imports from
"@fluid-experimental/tree-react-api"
to"@fluidframework/react/alpha"
.
Change details
Commit: b388c7b
Affected packages:
- @fluid-experimental/tree-react-api
- @fluidframework/react
⬆️ Table of contents
🌳 SharedTree DDS Changes
Added APIs for tracking observations of SharedTree content for automatic invalidation (#25459)
TreeAlpha.trackObservations
and TreeAlpha.trackObservationsOnce
have been added. These provide a way to run some operation which reads content from TreeNodes, then run a call back when anything observed by that operation changes.
This functionality has also been exposed in the form of React hooks and React higher order components via the @fluid-experimental/tree-react-api
package. It is now possible to use these utilities to implement React applications which pass TreeNodes in their props and get all necessary invalidation from tree changes handled automatically. The recommended pattern for doing this is to use treeDataObject
or TreeViewComponent
at the root, then withTreeObservations
or withMemoizedTreeObservations
for any sub-components which read from TreeNodes. Alternatively more localized changes can be made by using PropNode
to type erase TreeNodes passed in props, then use one of the usePropTreeNode
or usePropTreeRecord
hooks to read from them.
These APIs work with both hydrated and un-hydrated TreeNodes.
React Support
Here is a simple example of a React components which has an invalidation bug due to reading a mutable field from a TreeNode that was provided in a prop:
const builder = new SchemaFactory("example");
class Item extends builder.object("Item", { text: SchemaFactory.string }) {}
const ItemComponentBug = ({ item }: { item: Item }): JSX.Element => (
<span>{item.text}</span> // Reading `text`, a mutable value from a React prop, causes an invalidation bug.
);
This bug can now easily be fixed using withTreeObservations
or withMemoizedTreeObservations
:
const ItemComponent = withTreeObservations(
({ item }: { item: Item }): JSX.Element => <span>{item.text}</span>,
);
For components which take in TreeNodes, but merely forward them and do not read their properties, they can use PropTreeNode
as shown:
const ItemParentComponent = ({ item }: { item: PropTreeNode<Item> }): JSX.Element => (
<ItemComponent item={item} />
);
If such a component reads from the TreeNode, it gets a compile error instead of an invalidation bug. In this case the invalidation bug would be that if item.text
is modified, the component would not re-render.
const InvalidItemParentComponent = ({
item,
}: { item: PropTreeNode<Item> }): JSX.Element => (
// @ts-expect-error PropTreeNode turns this invalidation bug into a compile error
<span>{item.text}</span>
);
To provide access to TreeNode content in only part of a component the usePropTreeNode
or usePropTreeRecord
hooks can be used.
TreeAlpha.trackObservationsOnce Examples
Here is a rather minimal example of how TreeAlpha.trackObservationsOnce
can be used:
cachedFoo ??= TreeAlpha.trackObservationsOnce(
() => {
cachedFoo = undefined;
},
() => nodeA.someChild.bar + nodeB.someChild.baz,
).result;
That is equivalent to doing the following:
if (cachedFoo === undefined) {
cachedFoo = nodeA.someChild.bar + nodeB.someChild.baz;
const invalidate = (): void => {
cachedFoo = undefined;
for (const u of unsubscribe) {
u();
}
};
const unsubscribe: (() => void)[] = [
TreeBeta.on(nodeA, "nodeChanged", (data) => {
if (data.changedProperties.has("someChild")) {
invalidate();
}
}),
TreeBeta.on(nodeB, "nodeChanged", (data) => {
if (data.changedProperties.has("someChild")) {
invalidate();
}
}),
TreeBeta.on(nodeA.someChild, "nodeChanged", (data) => {
if (data.changedProperties.has("bar")) {
invalidate();
}
}),
TreeBeta.on(nodeB.someChild, "nodeChanged", (data) => {
if (data.changedProperties.has("baz")) {
invalidate();
}
}),
];
}
Here is more complete example showing how to use TreeAlpha.trackObservationsOnce
invalidate a property derived from its tree fields.
const factory = new SchemaFactory("com.example");
class Vector extends factory.object("Vector", {
x: SchemaFactory.number,
y: SchemaFactory.number,
}) {
#length: number | undefined = undefined;
public length(): number {
if (this.#length === undefined) {
const result = TreeAlpha.trackObservationsOnce(
() => {
this.#length = undefined;
},
() => Math.hypot(this.x, this.y),
);
this.#length = result.result;
}
return this.#length;
}
}
const vec = new Vector({ x: 3, y: 4 });
assert.equal(vec.length(), 5);
vec.x = 0;
assert.equal(vec.length(), 4);
Change details
Commit: 21d45d5
Affected packages:
- fluid-framework
- @fluidframework/tree
- @fluid-experimental/tree-react-api
- @fluidframework/react
⬆️ Table of contents
Remove JsonValidator (#25381)
The @alpha
API JsonValidator
has been removed: its replacement FormatValidator
must now be used.
As part of this:
typeboxValidator
has been replaced withFormatValidatorBasic
.noopValidator
has been replaced withFormatValidatorNoOp
.
Change details
Commit: 64a9b88
Affected packages:
- fluid-framework
- @fluidframework/tree
⬆️ Table of contents
Add configuredSharedTreeBeta (#25531)
A limited subset of the options from the existing @alpha
configuredSharedTree
API have been stabilized to @beta
in the form of configuredSharedTreeBeta
.
import {
configuredSharedTreeBeta,
ForestTypeExpensiveDebug,
} from "fluid-framework/beta";
const SharedTree = configuredSharedTreeBeta({
forest: ForestTypeExpensiveDebug,
});
Change details
Commit: 1e2d48f
Affected packages:
- fluid-framework
- @fluidframework/tree
⬆️ Table of contents
⚠️ Deprecations
asTreeViewAlpha
has been deprecated in favor of asAlpha
. (#25512)
Please replace usages with of asTreeViewAlpha
with asAlpha
- the function signature remains the same.
Change details
Commit: 7f1cb91
Affected packages:
- fluid-framework
- @fluidframework/tree
⬆️ Table of contents
Move IPersistedCache types to driver-definitions (#25518)
In an effort to decouple the driver web cache from the odsp driver a number of types have been moved from @fluidframework/odsp-driver-definitions
to @fluidframework/driver-definitions
. The moved types have been deprecated in @fluidframework/odsp-driver-definitions
, and any usages should be moved to @fluidframework/driver-definitions
.
The moved types are:
IEntry
IFileEntry
ICacheEntry
IPersistedCache
Change details
Commit: 54fca68
Affected packages:
- @fluidframework/driver-definitions
- @fluidframework/driver-web-cache
- @fluidframework/odsp-driver
- @fluidframework/odsp-driver-definitions
⬆️ Table of contents
🛠️ Start Building Today!
Please continue to engage with us on GitHub Discussion and Issue pages as you adopt Fluid Framework!