{"_id":"@fluid-experimental/react","_rev":"116-cc096b741939443737dd37b9bbee1f0a","name":"@fluid-experimental/react","dist-tags":{"canary":"0.59.1001-62246","next":"0.59.2000-63294","latest":"0.59.2004"},"versions":{"0.42.0-27549":{"name":"@fluid-experimental/react","version":"0.42.0-27549","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.42.0-27549","maintainers":[{"name":"danroney","email":"daroney@microsoft.com"},{"name":"sambroner","email":"sam.broner@gmail.com"},{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"0ec7ca1e85a2716d9dc3f0309957d18224044618","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.42.0-27549.tgz","fileCount":259,"integrity":"sha512-E0uYfjfctY1g0Xj6kpuGDrNNHXhH2v41LlPE2YPi+y827OStQPExnNUdQrx0S7oIRp33cDyabq9YacOWYAQ6lg==","signatures":[{"sig":"MEYCIQDHBbwizr3ghyw486FKNGnv3YIYcuZBxrgIuXXGIvxajwIhAPSSShbHm9MgFQVmVgBToE9Gjw3q2hqWzEOUXohDugqO","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747606,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgyhjPCRA9TVsSAnZWagAAEnMQAJT4Ic3txK9m6ssEmegz\nVXc9XPhEpXu33SrznjYa5/VYtTq2NiLGXTJpqu5IeLRGP0pFCGgDJSpVNocx\nAt0uZROgwVN23GcAfAxU3AuEGZZjOWO450lA/wQGmdnpZyaxrVEO/6niZ1h5\nG+mT2uMri6QvBezEgPwcTzZBKxiD/y1MeQ4edQ59pfJtkNFijykgubb/5YGT\ns6NLo8y3qNlgc1twtfgrJ0LUPekMIK8n/NiLKEfAFrl3H5bAjQfBGm21hD+R\nrTTcsLe63CQytraLBz4i7jbajn5FLrmkNdKDiKnj6To8eVuPgS7hTsr6g/iM\nT5NB5Pr6q0PnHD4ejHlfh330Nh7BiQV/URApeKginf19fwAEpw21MOxLNZ56\n9qx/2Fn3vpkTUzUS34Oa/2F6qoIP63xCH8fRhOTCZoY7Z3IJiMX1oEXN5+Bc\nbPfmLsZpKoM2wwM7WKtMJG9gsvnW1QvIWB9jvQwpZN9HKphwEpNSlZ1/CIiS\nM8BQFjicZK1zhdv2JiVxGcMRXPbOT1WvQAIRo7dpM2i8en7es+JlyHIz/9xG\n8kQAevgjqcGMbUguA71W30eggXN2Jn/oVp6/MF8JHVSwymgax60R0W0VAjSo\nz2Zi1Q4oEuxkg/TP2zxuWF58S8PpRjxUb2EIbSpJBrrUjw+SljuFHmF8fE6u\nGpAX\r\n=MHlX\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.42.0-27549.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.12","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.42.0-27549","@fluidframework/counter":"0.42.0-27549","@fluidframework/aqueduct":"0.42.0-27549","@fluidframework/sequence":"0.42.0-27549","@fluidframework/core-interfaces":"^0.39.0","@fluidframework/view-interfaces":"0.42.0-27549","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.42.0-27549","@fluidframework/datastore-definitions":"0.42.0-27549"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.13.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.42.0-27549_1623857358962_0.8560835931705966","host":"s3://npm-registry-packages"}},"0.42.0-27644":{"name":"@fluid-experimental/react","version":"0.42.0-27644","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.42.0-27644","maintainers":[{"name":"danroney","email":"daroney@microsoft.com"},{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"1adb916f6cb7a6794efd05bbcba9d5b16a5c000a","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.42.0-27644.tgz","fileCount":259,"integrity":"sha512-amrWorguRJeluqywf8oKkZF1A1XZpDMrfgJMJCOpyr8T4KutqX03N+gwrpRMYuZMY2/n6nKpiQ/laCBbBrUKcQ==","signatures":[{"sig":"MEYCIQC3o8sUcG6Fus0Rk8F1EWwQJgg3qG/HERJ0LbE2g/Il7QIhAPYLJn1CDOTjOOQKtsslF+GACgkhSBlx7BcBE18kE3cC","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747606,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgynJVCRA9TVsSAnZWagAApfgQAKKZDo/VARlEPy9TWC1A\nkrO6teqYLt8e82S71Wfs9NAjOFpNXuNmkEpt9sAbINvJV4Lxfx8xwBlFrZSC\nv0hwk14Nnee+e5qQ+LNLp/Msd7nZeZnwEhNkbJuldpAgi9nYZZfWXbBV/kM9\nnTqfa2/vzYEvY42DPsgsGA3mxS8gAw2l3tt62HB860kabV68jL514S69X0Oe\nGoCbckRwFzZ15o/84XaCkN95+10zeiR0WOSja0tMYXm9GKn5G7flE/AROcHF\ndVEm2RY84gDfXD617CHLJKSMjnxm642d8dDZjce/vP+xFqsULb+1IlcVH5u0\nWOlem+uN4JE0h0dawQRbshATS7FM3h+p5BKpRKfei/6zJkyd9pNp3uF0VM5W\n5u4Axoqrb396vJLazBpvub971DZT96Olos+ESTTm7dDeZI/Iu6MXC35h1fYO\nMqMoI8rme0409GpiJhI1amfWtZlxvcygbvqB3H/EUyyOu0Qx2j/qFP8nZmGk\n5KP/SIxzc4KCutqcY8KLsyV9TE13Zq9imB1rDX3VvfoVj3L71XSZS/lKcOUU\n66lYG2PQIvqyfFBeQv083p6wRMzBKzoCu2TCBZbYx4oPTxltkxhBAFpx8M14\nO276mTrGDcOGD5170KfuOyvTR7EPrcCZvIpOTSQMmyqgLcvmz50DzMT9iTqQ\n68WL\r\n=LbuJ\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.42.0-27644.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- An implementation of `IFluidHTMLView` where users fill in the `render` function and the `IFluidHTMLView` interface is already fulfilled without any further code. The React view used in the `render` function should pass in the synced data object for the view to use.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n\n    // Renders the React view\n    public render(div: HTMLElement) {\n        ReactDOM.render(\n            <div>\n                <LikesAndCommentsView\n                    syncedDataObject={this}\n                />\n            </div>,\n            div,\n        );\n        return div;\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.12","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.42.0-27644","@fluidframework/counter":"0.42.0-27644","@fluidframework/aqueduct":"0.42.0-27644","@fluidframework/sequence":"0.42.0-27644","@fluidframework/core-interfaces":"^0.39.0","@fluidframework/view-interfaces":"0.42.0-27644","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.42.0-27644","@fluidframework/datastore-definitions":"0.42.0-27644"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.13.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.42.0-27644_1623880276706_0.062578933143421","host":"s3://npm-registry-packages"}},"0.42.0-27677":{"name":"@fluid-experimental/react","version":"0.42.0-27677","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.42.0-27677","maintainers":[{"name":"danroney","email":"daroney@microsoft.com"},{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"0f89d0c1b920f04ac03f1d9842761dca66312b76","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.42.0-27677.tgz","fileCount":259,"integrity":"sha512-qR1CK6Ia3uSpg0UbmFZW//6H+Se73IafUblLoTcpUomQysIx1zzkGMx3X/9zRHsUQ/Du1q0zSbWQA1JBEieYBA==","signatures":[{"sig":"MEYCIQDV9TpgAwgM86h1DG06L3N9pAmh7PK/5f0HG4Vaf+aExwIhAKu6LrfrZSWObjsjMSq1A9VIUY/hbMDXBK7INIVmpJuP","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747606,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgy2NeCRA9TVsSAnZWagAA9yEQAJUKuSSFwwp44n7jCM+l\n5gjw8kxBosOtryFiQV3BC50Z2Psgf/0VacQfMo2ORLElUCR8oyPjIqfbi6Gj\nZz0tExIpfM4Z6r513fMAOmyPKNG5xh0gD3hcJ6d/zvPCNVBL7WKOJfDKhsAY\nOUk8DDqpLnP8cWBmUW3AerzscVWwrNxIyho0BGszEjBo+WeDXUKNC2E80Dda\nxv8rQV/SVwUXxWwy83LfdGiLiQgrTEJ+CjR73IkjTIBFXJyqw4SykCEVi3+k\nLGQFMyFyb4CM7zNM3wZb8JOXxFLbq04FdmpadNwm3fbUsdOA4OZDnv5n+9ko\nr/bJOST0bRNA4W0wPpd54fvxVeSswPhakdSbaBbEFqtlJe4qZCJQ4f2JMqU7\nJS9c8KsADAcAxeH1slm4rIybRGuMsWouNKOlIJQofMl3xZnWDJ38L/6tXPsC\nD0ijQ1xVQNXehN8GIYIlcLLA3RztL7TpsGWAAYrhHtdJhDRG0iiK6IYn4u2G\nh3RWQ412hi/7ITbDSLXGDOA4YPyZuiniHpNG9ALlhEFvBD4IjT/KRR1oXa6i\ncQUFfYls/Cdrd0gEzhI5coIuTSejvxwykLxslxkRao9pPYKclHJoaOEAMDLN\nAZXyGlEwWbgJLNBUJqKDfIOVl/joxQ1OmFFLJU/LPqEUGtbUBbInY/+1Hikp\nLHvu\r\n=9IL2\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.42.0-27677.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- An implementation of `IFluidHTMLView` where users fill in the `render` function and the `IFluidHTMLView` interface is already fulfilled without any further code. The React view used in the `render` function should pass in the synced data object for the view to use.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n\n    // Renders the React view\n    public render(div: HTMLElement) {\n        ReactDOM.render(\n            <div>\n                <LikesAndCommentsView\n                    syncedDataObject={this}\n                />\n            </div>,\n            div,\n        );\n        return div;\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.12","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.42.0-27677","@fluidframework/counter":"0.42.0-27677","@fluidframework/aqueduct":"0.42.0-27677","@fluidframework/sequence":"0.42.0-27677","@fluidframework/core-interfaces":"^0.39.0","@fluidframework/view-interfaces":"0.42.0-27677","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.42.0-27677","@fluidframework/datastore-definitions":"0.42.0-27677"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.13.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.42.0-27677_1623941981714_0.4024121350901846","host":"s3://npm-registry-packages"}},"0.42.0-27683":{"name":"@fluid-experimental/react","version":"0.42.0-27683","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.42.0-27683","maintainers":[{"name":"danroney","email":"daroney@microsoft.com"},{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"8c1608acf3faedc452dd60e976b45b2300c2295a","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.42.0-27683.tgz","fileCount":259,"integrity":"sha512-5uGTGZZE3PDrW0vQ7ZNhjhVLvKJm/TQNlXyrWCyvQFUWy5MKzqfSjOO4vx6I8rxvvJ/nv5Q4p931N141V4mARg==","signatures":[{"sig":"MEYCIQD8VoosS4M7JVKuHtYyughup27LoWJENwIXul3i9ZXM8QIhAMAhSsSgEOV8KwMQvHD/nC++/bxkMTrTeAf0f3Fq8cf/","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747606,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgy3hACRA9TVsSAnZWagAARVMP/i1Ei1MUKCjBtKOgCczS\nsYBrQG8Yu7QuAuUONnmkGsAo1eJJi088CN2MsucbG5MvDqz/G2tEl/ATmQ/L\nbASrq3NGSpEGd3KOfknqOk0jB4WN4cdKRxa2YrZsgV6xnh2cZ6nGQRTmBrM6\nxJJtw9QSltbD51WQ9cEBAYhteOwJlwoNM5YJZG7aMXH5iCT2e4ooy9R14nGQ\npdQ6cRW2hqpjNCzHNKRrga9E7BRFrAGDv1PEyZ2IZKfI+UJI1dgTbeci+Ih7\nJjXyfhJl6Gwx8ETws+TjAfLISJbTDx2ydaGAg4KOjiJP4EUtom8hAMjGEFVE\nGQqH4SkSiXlxRIZ2LkvBnDGMMGAVjWpGt2KZk0W7S5hQg5PrXRgHUkzU8d33\nnc7u2SB8ZfAKd+Ive93fwG5dT/41ElI+20Eey9Vf6B6esTaKf/7Hy7r4cg+Z\n5AIJ9gvY9VYA3wMSaNJjNwyT6JL9ZZvKZqXbXUXQX1WiDr5da9QTCmLpUcUX\nFMAN1G9/CF6RIDxCzHJ10Wbzz4cFQI9Sp6DCwCvCPOPzvUYv2MZEq728neVg\n51doAwiogRMiY+XESzSI016uAJYkf6O3pywy3kNQy613JcBxJCvYWdphE/Hg\nSqfD+PsNmTYt5z2K8HZ7KobAPqQuNPFcXuPEJbLABaeWR4VK2AQNBFE09ZdZ\ncUHe\r\n=N+kZ\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.42.0-27683.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- An implementation of `IFluidHTMLView` where users fill in the `render` function and the `IFluidHTMLView` interface is already fulfilled without any further code. The React view used in the `render` function should pass in the synced data object for the view to use.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n\n    // Renders the React view\n    public render(div: HTMLElement) {\n        ReactDOM.render(\n            <div>\n                <LikesAndCommentsView\n                    syncedDataObject={this}\n                />\n            </div>,\n            div,\n        );\n        return div;\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.12","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.42.0-27683","@fluidframework/counter":"0.42.0-27683","@fluidframework/aqueduct":"0.42.0-27683","@fluidframework/sequence":"0.42.0-27683","@fluidframework/core-interfaces":"^0.39.0","@fluidframework/view-interfaces":"0.42.0-27683","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.42.0-27683","@fluidframework/datastore-definitions":"0.42.0-27683"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.13.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.42.0-27683_1623947328147_0.01948934494486365","host":"s3://npm-registry-packages"}},"0.42.0-28410":{"name":"@fluid-experimental/react","version":"0.42.0-28410","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.42.0-28410","maintainers":[{"name":"danroney","email":"daroney@microsoft.com"},{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"2b21a7033397a6770a4bda3157feb4b3ab8bbd5d","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.42.0-28410.tgz","fileCount":259,"integrity":"sha512-j5qblnR/GNhvoVMGP8ySRPWqGkzD5TZaFHMZ//Cf1fCrWTmCtwK0rACgkflCeAUL502dTdWI+Kb+XQ6/yE+k9Q==","signatures":[{"sig":"MEUCIAaVQ98GsUZF+X8bgF1eap9D+NedJDCi6Xv+vnaVJjJWAiEAqquj9aOMZ0FF2sBqmndbqDMdXOyS0q4NTNAXchyuiJA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747606,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg1kIfCRA9TVsSAnZWagAAa0gP/0KssivieYEl6BAH6xL8\n7Taa4cvqyb/lzmblgF+oVo9FMGzBjiXGXKPBM6mfHyn+Yadx77EOE9pkSET0\nTNYBkhcURXslde1FBmsVVFt/djgOyaV/ybhzdgrX+pwXy3lhXhSQ5CO0QS3j\n6M5PGlwdId0x8wkXECil+5r8Zy2AAROYdlHW0HEUndaYt4TpcYI41fjUNjOG\nmym6ceLpmexzMs/5VYl8lazhHfLYMfvGg01muZ2h0n3LF9xK8VLn5WeCZ+jC\nShSxvV2EWUFaF1C1DososS7l3eC9asnO3HiXKEn0m1clTQVuKJkRGVtI+rX9\nFBLJ5+fsustkcEwIOE/bDG5XI3OTG8Hj/Kmb2UWDpVa6FUFSL8UJKpRQAmll\nGsQyFhGU57WiHqUY63akmJSpN2KGDnKViIai/q7eBb04BInJ/TzjQK/bZvno\ncH6OtRXlfrpYBzZVJjTZxOX6v6st/tlKCgDefdUmOhor/QZDh/GYpWVpbIXU\nkn5+Rcf5r4ZyaHjXhOM3hqb5C4W7QMUo9SdtWF9A9U5E66TGKgRMlq+MjMT7\n/kWTtKOTkr+eXyrKZK/PjaUjXEw1AEoygJAe3SabZeWzhW3RKn6iYCigZN+B\nkMHPpVtD+do2GGNI2FLlz9jywzUp7+mNUU/dVflTKyWChZcH5SRRYfmotH23\nlL8n\r\n=S4xk\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.42.0-28410.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- An implementation of `IFluidHTMLView` where users fill in the `render` function and the `IFluidHTMLView` interface is already fulfilled without any further code. The React view used in the `render` function should pass in the synced data object for the view to use.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n\n    // Renders the React view\n    public render(div: HTMLElement) {\n        ReactDOM.render(\n            <div>\n                <LikesAndCommentsView\n                    syncedDataObject={this}\n                />\n            </div>,\n            div,\n        );\n        return div;\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.12","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.42.0-28410","@fluidframework/counter":"0.42.0-28410","@fluidframework/aqueduct":"0.42.0-28410","@fluidframework/sequence":"0.42.0-28410","@fluidframework/core-interfaces":"^0.39.0","@fluidframework/view-interfaces":"0.42.0-28410","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.42.0-28410","@fluidframework/datastore-definitions":"0.42.0-28410"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.42.0-28410_1624654367095_0.3178731101469894","host":"s3://npm-registry-packages"}},"0.42.0":{"name":"@fluid-experimental/react","version":"0.42.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.42.0","maintainers":[{"name":"danroney","email":"daroney@microsoft.com"},{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"a558e861a747f8e0a7d85e7e9ecb3d06a7ef4151","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.42.0.tgz","fileCount":259,"integrity":"sha512-yR5cdQ7uTiDU1UcwAwmz9zI8UuX/wJTFWupqHICbE4jvLN14Tzp+sVlmVHtHw7Z9AyXP6Cz7qipITFboYSuNrw==","signatures":[{"sig":"MEUCIC1TT6JJ/Piutvh4LcDzpkSil5UJIxvPgcFvpJ5TU9phAiEAmY5zuGFcjC8UazWXnrLMJxYCrDQjqC93MHoe+xetoiY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747565,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg1oiLCRA9TVsSAnZWagAAg6YP/iH0hWK+ETDnIjfMXj4M\nskfccpDG8ZlZgX3Qg0n+VnrSbEuerymWPoVF+Stmw/4Hoam0xKeArHZkCI+n\nJxRmLTyUWrMnp6WhdgwLvP9ZkKUYDZ1j1ZuqNH7KROzXIfKPHTHQsbX7bjvd\nml9sHyt/ppkn/p7Kku9ZvqdPsBkAhzmzN7En1gtD/NPo4KO85zanz2yps/kb\nYTM0WQaEG/lng48nDbS3JPfTW0KLkW4RGPSEHEFdl+A1/0pmho9dvctZbOqZ\n7iRkuTkoJcGh6DmabAwJrdq8A3o2SMXV+BWE2dj0JZn0wNr/3lM0IAFoOxze\nnKCjFThR2VqHj1DmQ7HqF9+Z7JdD8zmNbDsosxQTd8AfRiEPSboPgT2YFI2L\nA1WGpPqKl7iKBfap5ads1O7VHODu5H0CrhkjT6LfCy4IgBksgADnoH4AJedV\n0GeiATkhV0hyMf0RkOTxdL2H/xw9a10Sycw/SU38d+wSQ8zA3YXrg1uYfoQd\nXBUVwZ8U4XrkFmDN3SU5KSQhmKDbtggYo1h9eCHeqrL/oj8O2CN80Hga6mvz\nD1HGw6kV6QSiDVszBh1iK0hMzNDQUpSGeB2PegIe2iQeCOqiE/2kPRpNsHxV\nmKAAP2pZ5mODZDly3hdut5MDYCeEsP5tfU7Hj4+bDmtuA7p7RhDzL0CuwU27\nQzAf\r\n=brEM\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.42.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.12","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.42.0","@fluidframework/counter":"^0.42.0","@fluidframework/aqueduct":"^0.42.0","@fluidframework/sequence":"^0.42.0","@fluidframework/core-interfaces":"^0.39.0","@fluidframework/view-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.42.0","@fluidframework/datastore-definitions":"^0.42.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.42.0_1624672393903_0.23872866385992153","host":"s3://npm-registry-packages"}},"0.42.1":{"name":"@fluid-experimental/react","version":"0.42.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.42.1","maintainers":[{"name":"danroney","email":"daroney@microsoft.com"},{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"f8a81a57d54d532c989b73bdda4880cb80da0c26","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.42.1.tgz","fileCount":259,"integrity":"sha512-VjqbT+UW4ZrJ2l+ErqTjAt/yrhi6gjjuD1M926XpgSV0LNRPXEkbUPE5DgXn83WB8rRUS7KuK6UhM5/vpyAvnA==","signatures":[{"sig":"MEQCIC4bQ5B7fwrZWuB22SinZ6m8bMwAdiPIYRq312tjRerYAiBzBDwy5pppmRWK6BaWSABJhhaUMzrbIpkyZmfjlcbzhQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747565,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg5yfBCRA9TVsSAnZWagAAjwsP/iRvGkitFVxjdR3nYucn\nWbPd/3IIis6fvKBzwPf3i+NVX4Zz22du67DZWHcGl7f1YHvRBdxAs4s80Qno\nN0Ir3YPRPWAbnb9PjAr8UfSbveJEQwtUxd6tSalN3+Vt/FVcq1w5BO1EMxUn\na/aWcsaCEupetNqwmfPN1zXOWzrdKHcB9ew4qImdW6XUUPiXeGtRhQsSfzDA\nH+WxNJGqMlJhi7usMvp8q+9xPnnOCRxqMyLxf/dKtX0srY+1g7yWWzNuUGfk\nZLHdNGdVeijOzM1gukdGFEyEgazdUJLTH+oLZ/azXBpEfEa4aWLynbezhIGo\nCxM66bvp6eo74s49xQRHLx3FnIbgJbNP5zPZ2YyjFdi4aZ21hOXu8Xza87xO\nrvlkHILhFWzfZrJ+I2MzOdD5b6r/IZE2N/0YdCKrGX1kzC9MVBsR2q7hjIHo\n6XXyP9U2IuMCftIkERPYH8aHcUpiUUc0VPcyPgY3AhZ6nGUeC7r8JpVHxfsB\nbBsxFu2ylaOXtC/MPeOIDWJ6s9EEIOfbki4Kyl81o/8qGvaFjoUvzxQ/UdsL\nA3gHlhN+hlBsMCu/xDvmPJ6KOIUEUGAQEFmb3yuQOnvrAvKsi0cVsb0Y19wv\nK+msi0PzfWvS1t1vWx+QoOrV3I/FsvRWxJV/Cs6Z53jqEsgnpETlTPTpWgQX\nUE2N\r\n=5s7h\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.42.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.13","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.42.1","@fluidframework/counter":"^0.42.1","@fluidframework/aqueduct":"^0.42.1","@fluidframework/sequence":"^0.42.1","@fluidframework/core-interfaces":"^0.39.0","@fluidframework/view-interfaces":"^0.42.1","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.42.1","@fluidframework/datastore-definitions":"^0.42.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.42.1_1625761728875_0.17100826480283793","host":"s3://npm-registry-packages"}},"0.42.2":{"name":"@fluid-experimental/react","version":"0.42.2","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.42.2","maintainers":[{"name":"danroney","email":"daroney@microsoft.com"},{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"0e2e4b5a3a38cd848ae3418875dd1b6d0936e348","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.42.2.tgz","fileCount":259,"integrity":"sha512-o0ECufkcku0u5+NrRJ88MxSeuTPTDnGG5i8oQUToJSqhKL82qcPZInRr1dFfS5cyuOwbm9BmQpkf8ZbTXGZO3w==","signatures":[{"sig":"MEQCIFIOoo315cWnbJmY5bfTkHV1Typ0l/+agz9lDLab4wiUAiB74s1nw5qB88WCPzA89DyD9MkG7WK75F/+ipMnjrc7hg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747565,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg54HnCRA9TVsSAnZWagAAjRsP/0lD5ot84YOArz8A8FGW\nIuWH8Q6O2m45a7UvQ9lkhJaXZIifCIC3XxskrZHkugAfUIc+Fbyya8gjW/4U\nsjZvSPyuxBjQ4j0K87GR8ETRftA84XlmZsrAqWa+Vbx91FKkZDgPMcktOadJ\nrbvhIYrhrMCW6iJg2FAPwLXL6iohmxnJ/5IH7hPGRGw8FtEUNsT0NI7Gf3L0\nE5AyAes6grqUmBgxKW9qOGNOTiavSqjsCvX7m/DMK0NmbyPoL5D0mJdc8NNO\nSFFj/tPxSCV9npAc/GZx95ZmnTe0FSjJoSjU8HSdAurltdLpETlXSVGDZoa7\nBSL6NOI6Mw7wFeNx1+NLFIyE9tvX8/QhRjCYGSWEX93bqu98eFYeyHVGELhz\n5o8CCQG+RvAd25n+/r/KZR8evsFUMv6bdQj/lZ55kV3AwYXdZ0yvNpK9jRte\nug6Slq8wxrshwNDKfR/Idb0MqIyOehqSvGqutDSe4lIcPXJQViYQI6BoX3S4\nS7OUaLdnqlTpysx21CCP6YOvCP218PyhFsZuJdZGT0DhiLHXv6WEDvKL5mC6\nhClYK0IOPbTJ01lhX+t7jwgGhq0pS8WIrOdNP4JECuVGTQnNPZ2GMFuCXHyC\nLzLm+NU39qTk3LDXMq0X/eBkRM168X52acyOWNpsa4KYWYaSmREvxzBhgbFu\nOK+7\r\n=49NR\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.42.2.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.13","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.42.2","@fluidframework/counter":"^0.42.2","@fluidframework/aqueduct":"^0.42.2","@fluidframework/sequence":"^0.42.2","@fluidframework/core-interfaces":"^0.39.0","@fluidframework/view-interfaces":"^0.42.2","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.42.2","@fluidframework/datastore-definitions":"^0.42.2"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.42.2_1625784807264_0.6700216852439351","host":"s3://npm-registry-packages"}},"0.43.0":{"name":"@fluid-experimental/react","version":"0.43.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.43.0","maintainers":[{"name":"danroney","email":"daroney@microsoft.com"},{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"53cb34a9dcda36c97735c4d4320fd9efc1738ba2","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.43.0.tgz","fileCount":259,"integrity":"sha512-H0X1XF8Ba4UYaiuU83f9RbgRfZVFmM0qXDnSXqsnQcG8KAHg8K2/bdSqmNe0N0bcgNd9f4VG9JaOMVY5KLFHtw==","signatures":[{"sig":"MEYCIQD8YhKtFrAUp2YakhF2U1zvXZWT3ANHH8WQs1sLudgPDgIhAJAvQH019r4u87wdtoZBrmBEs4Ei3K7hcMvd4nqWJ7+R","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747565,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg6KitCRA9TVsSAnZWagAAT0YP+waPNv9neURr/1pCuaGq\n34AOESmVYj1Ywoq1VAzRxMotO1U+f2GVPvO1PfMaBnPnivNa12Q6CpCT3FCq\nx/lnF3zSUGpdypPTGd0swMn6/YZTnn/NiHkybISX99mZMzlpFDMQVdx0YmIp\nlZyncXMlpwbXGVTdHTvjJAmg78/JAG/ZFwF2Fjwp4cPyjnIgOFkvGxMhGVgx\nBdPqmKKb2m1Re2ZSwWA00vYNl43Z2PQB+hAP7TJvAl1HWF2VmoAgYGCbhoDt\nPFfs4KpKEcUFlquWm7RdeWO1CwRiIQYmv6529+V++GE/CV02CkQJ5of7Px8P\n1vUalgoeL/sKXKsFxSmKFtyzEGzXHh/2zthvd2XF0eENxRCuIK/mePs8r39B\njugDUEJsvM0EWpDA6DUD5CBIwBGxu9hwAY3M/DV77QJZPClATWPcDG2BfdoR\nmscCt/tiMhdT8ZvFcVySbYfnECicuMsKVcdMEMYtOqCOSVJTuI/stLUkKBh1\nCv7rfalvEynorNa4T2+EXq2pFDOk2ZAkVXJYMvNsI98AEF7GaUpYgwyxMCJa\naTmhYQ98t0r4aB2u6q429tSeBkQIX3v5TdFA8J2PO+bVzzPlw4JuIFibQt1s\nDcWU5NgvoK0T3cOGpgg5tpF+ow1zn2Ww+gEXW8kdRj9uR3njIsMrcoREuae7\nbjda\r\n=EVQw\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.43.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.13","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.43.0","@fluidframework/counter":"^0.43.0","@fluidframework/aqueduct":"^0.43.0","@fluidframework/sequence":"^0.43.0","@fluidframework/core-interfaces":"^0.39.5","@fluidframework/view-interfaces":"^0.43.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.43.0","@fluidframework/datastore-definitions":"^0.43.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.43.0_1625860269393_0.6515025444434013","host":"s3://npm-registry-packages"}},"0.42.3":{"name":"@fluid-experimental/react","version":"0.42.3","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.42.3","maintainers":[{"name":"danroney","email":"daroney@microsoft.com"},{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"35ba972fcb18d92b05e33e32664611143efbfb44","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.42.3.tgz","fileCount":259,"integrity":"sha512-vKPYEYdW4lrHV+hS9KSdJBeo8912+sxvQYZ6rptlt88HC0aRv7jXOztRIMJekHBqXf9rg88T+YK9jcUdnFz49A==","signatures":[{"sig":"MEUCIQCbkxrekOEtjdMBNn5VHZPP5xWM7rVXrSeHIr0JxcxnGQIgVtu3LI8lASdCdUot5o0E7MAoL0zv7hF6aL/koy45+10=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747565,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg6Lf1CRA9TVsSAnZWagAA3rcP/0p7hrV1fcOUyUF4RiMS\nGy4I+gl7aUCMxiHExLBfG53XECp3f/c026pVCG0g6Ai81X7Er/4qahLZFJC5\n6kmmt4RgyZGQpzVb2GyDvjTSIauDlhbX0D+039b40XhWZmWz61gi51rMmsH4\n57qGdK3K5OBfd1bwjCSD9dMFdf1gxsalxnwllafi2rqpCrTljmbKgprlb31d\nHKyw56mDe3cZhmbJU+3mV0jY11J6D7e2iP8eb6/2ijR1yUEQo1VkEV9wzgYi\nKHs6cMVRlialnHo6R7V+ecRByAwjhNVNDHPWwU3v0OH5NwMLhV7sysV6mAAq\n9N/3kRIN4AcfFLXPinQ8qK9UOC0BP9SObmYodMVvsljPMB1FGuCNkarr9fKY\nm0SqtVM96WK+N8OtYngIq4DZw6dWduHy5XyuCJQTB8fJcWgSykdBUEzlLjuP\nB14fWemI3xjt/6anaWc5juWbZe6HXWKHmVZ3kuIDafUFWY+zyrltQ9i0JreD\nwu0dQ8P8XvgK0DYMmk4Zs4JQAp0yUBLfp1ChrOdCYAJEr3aLZGfJq8lzfx3H\nWxcfmTATaNQHbKTak1i/SIoH0ZJXPL8rPwcj5bETcd/m95kgy1rk8nLWqpZ1\nD3/jVPCEU9JZcT9ADQdDq9jQ1lDj9Ipd0zTFacrWRxu/w5CSunM0KtrT05yX\ngk9e\r\n=c+AC\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.42.3.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- An implementation of `IFluidHTMLView` where users fill in the `render` function and the `IFluidHTMLView` interface is already fulfilled without any further code. The React view used in the `render` function should pass in the synced data object for the view to use.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n\n    // Renders the React view\n    public render(div: HTMLElement) {\n        ReactDOM.render(\n            <div>\n                <LikesAndCommentsView\n                    syncedDataObject={this}\n                />\n            </div>,\n            div,\n        );\n        return div;\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.13","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.42.3","@fluidframework/counter":"^0.42.3","@fluidframework/aqueduct":"^0.42.3","@fluidframework/sequence":"^0.42.3","@fluidframework/core-interfaces":"^0.39.0","@fluidframework/view-interfaces":"^0.42.3","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.42.3","@fluidframework/datastore-definitions":"^0.42.3"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.42.3_1625864180782_0.887822903555882","host":"s3://npm-registry-packages"}},"0.43.1":{"name":"@fluid-experimental/react","version":"0.43.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.43.1","maintainers":[{"name":"danroney","email":"daroney@microsoft.com"},{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"4c4f90a7b86d6b5df8c7a4b98016bf2021f98d79","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.43.1.tgz","fileCount":259,"integrity":"sha512-Sn/p6ib9NBBHlE0kB4+uJXTtsrW8sTGWM4ct5M7bWQ783XRwsY2n8Qc/Q1vFXEyZrNvslccvAryw6jDWj9l6+Q==","signatures":[{"sig":"MEUCICSJAVBmLCU6VP/3LplklUBWLijA6Hf9a+m/tVQgPHwJAiEAmGcd6B0OOFzwV9T5Ex9pGo24A36gey5Y0p8YofozrFI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747565,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg9gcICRA9TVsSAnZWagAAWgQP/RWFBzTpp6Pqf5DT1PkB\nxXtsE19QLMdFUx+fRS7bB9ZBIeiupTZgsyiSJTiqbt3sbz7uyOACJdLgYsW2\n7qF7N7a9j7ugxuSSkwX0+fi2njUndegFoAB+czZIOzsiue6AgC7KbD3p/k3+\nelO+FXbF/gtKJXe9Q6w62qAEVc+/w/pS/W+O1nbus5UT6MFshfen+eMZhRaI\n5gE1AvlBob2NoXRe0uwFgwcb7ab9u/pbwXfth+N0PDNxiwmqCTqwWndlDgRI\n83OA7Knud1tYj2x66GkYOwRueELKH7244iaKL/m8r3KJOTDQSs4qP87ApoAK\npyh8ana1FcJdOdahgTW1mpfAS3F3piNloS1fppOrxhIe/FHvb0QnmPfg9fNH\nqSGdIJ3cSkY9PKcUbPuvjP+mmTByuYA6ZEa1J3ElR0dl2m0mR9Vm9twH9aEW\nPxEmeNmDBjXvlkyQ9NuPGsj6Ao0ymsQuLwvPmAofKI47Ma3TWlLEox5mx2CF\nSSDQxugBE5U/fhN2lfFRHpIVtk0FL0Mv5y3e76zdjv81CHN+z4wYTA1xfU/Y\nYrJJNryJwF6imE0yCX32FbC7QCAOMEG1hZScErG+jg21AapdTi4b3OYJ37cr\nFOmxehHtY8uJ898DQVWIHpuYulkl2TA3p7Bz/rOgq6XsB9cyHcro1vRIrXuK\nXhqk\r\n=fBkb\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.43.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.13","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.43.1","@fluidframework/counter":"^0.43.1","@fluidframework/aqueduct":"^0.43.1","@fluidframework/sequence":"^0.43.1","@fluidframework/core-interfaces":"^0.39.5","@fluidframework/view-interfaces":"^0.43.1","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.43.1","@fluidframework/datastore-definitions":"^0.43.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.43.1_1626736392421_0.4419119114729946","host":"s3://npm-registry-packages"}},"0.42.4":{"name":"@fluid-experimental/react","version":"0.42.4","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.42.4","maintainers":[{"name":"danroney","email":"daroney@microsoft.com"},{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"8b6822d2714c6f598450c3082b65b418b0a65097","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.42.4.tgz","fileCount":259,"integrity":"sha512-zOcio5/I9yA9hhKyWcHUo7ZfWsfBPrU2+xlnfsfhi1BK0RTayeqbWrtQxCrvV+1WSy8lYwoomktNTZzV8IUrgw==","signatures":[{"sig":"MEUCIQDcJjZve3xCDcq2WskOqmMAPVtC2nJEz+zNlRXIb9APcQIgCsMyv5lHujkMWS4cfiIf9R+3JTqK01cw5RPw3tqQwL8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747565,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg9hV3CRA9TVsSAnZWagAA/c0P/3N9bDEpPb7fELQVOf4s\nB+uJ1k/hp+ekLf2XOzz46EhOOquIPbXzE6Tksh1Z7eNO/yWAuReOQQ30t7ro\nZWGjoS81tAgsVGO/Rc6tTsgLUJuJXFq8u8zY2tHBMk+FGiAOYAfEuUfJYi7B\nDEwm5XY/zyL5Wk5VO0WcUY8RHPGOklDvHVLlRMbAralXClkzd061VXIiaojL\njjg2P3LFgHxg2zD40qpb9L90VydVGpP5JCiVUbyrpXrAQmL3zew6tiHwS0y9\nrQu/+fuur+K7r1BT7fOZd2uvWm7nLG44LGDFdERZ30if2H+O22g3bpL7Duq4\nRmP/8gvQIemJP0VaBTEOhGdt1wIq04aX0q2OBuVN/ldsUHQb7TO5oJJX1U/z\n9CN7CprPBhRizYSGPwCutU36fbiLgR5uiqopOu81NBzJ9brxZ/tVHl71Z36M\nwy71DUBUyXUylHHa/iMOzXsbQDxKOXtHwUzIARAeTW4nV++5gH9KatwUBxJv\nd11Yjt8Mn6fOqrV+UdOLDsbKeKQsCoyoqtPkDJBmN9PqNj5uZzgee3mFeL+I\niJMlsMPUfgjI3tL1Jl5hNE9K3W6LO33UPsOx5oo29O/x3u3BClYmf1BR+LbO\n/jyC11rD2OYrFR0+NU1adVzhUpkaDrG0OIdpLgyuAae5PNSMtxjTsO9M9RSy\ndTi1\r\n=/uB3\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.42.4.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- An implementation of `IFluidHTMLView` where users fill in the `render` function and the `IFluidHTMLView` interface is already fulfilled without any further code. The React view used in the `render` function should pass in the synced data object for the view to use.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n\n    // Renders the React view\n    public render(div: HTMLElement) {\n        ReactDOM.render(\n            <div>\n                <LikesAndCommentsView\n                    syncedDataObject={this}\n                />\n            </div>,\n            div,\n        );\n        return div;\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.13","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.42.4","@fluidframework/counter":"^0.42.4","@fluidframework/aqueduct":"^0.42.4","@fluidframework/sequence":"^0.42.4","@fluidframework/core-interfaces":"^0.39.0","@fluidframework/view-interfaces":"^0.42.4","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.42.4","@fluidframework/datastore-definitions":"^0.42.4"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.42.4_1626740087534_0.7362471372816237","host":"s3://npm-registry-packages"}},"0.44.0-30858":{"name":"@fluid-experimental/react","version":"0.44.0-30858","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.44.0-30858","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"f028dc4d91423fee306758f398d617909d8b0259","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.44.0-30858.tgz","fileCount":259,"integrity":"sha512-hrPdmyBkXypSPk6ct4a4vE/hHD2jaNS7hPKUzPNSznOPkBm9yK8cnw4ctXUF/0RBCDfEnl4RcL6LhyL2dcrN5g==","signatures":[{"sig":"MEYCIQCWlIFfY8fP+52FO7CMmsTsk7i6oQyUP2mzEmE7xf0T/QIhAM0mALGzyFkg7gbmkHAuOVl9AW1pU7s2Su8esQVGnuWU","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747606,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+e0KCRA9TVsSAnZWagAAlvIP+gIn61yuCI1Au+fE47Ov\nLWq/HKhBLJCxj3qSy5IT1kUjYGwVovRCdHPffvw9uPqOYARq2uZSlkBBWxQN\nQisS1tdfmZT9/Uux7I8er0GmyMb54uz3Lqo1qE7nM5j6Z1WwatqwLCb7WuI5\nvgLftL15icFAav1szFMdH4+XUUDDOGb1Xmay9NiusrRFju52A8fYugdqbzQY\ngWn3B2sqdB1V7Ze8PuumljErrQbOA5+ydZDm04wy4JxdXAg4/9HWG3boIBmT\ncNuF+9p2kvHWfUUX7EBd56wxYRsnlzP3FA75eku2K5mBQbZC/9nQ60dVnM5v\nMLT4hWBc+4toFmM3+FmZuZwSXz3zRHkGJkKwJhWvvvIj16Z+XWCkMxTtUqHs\nnirfYJPD58zBTYBLw8EK0G97apG5radnVk19qXci3sLaTQ0qlH29rzF9Uw26\n8XaaQSKeEHjK/aN0R/JTH54qSN3vwkv6gROzwA36oPN/5rc5QLELZiiSQXV0\nlRLFJ7QonwB8L3Zth2OMaJzHlB5Pw8sBY9U1pRhXN/Yltpqndb9xdR+V4hFN\nNeMNPUm9KOjXQ76OP8X81O0F4M6HLmluCthz83bOHtKG+c9kcr/8lHZ7PZkW\nDzRl212LIy0VyGb0DhBdF94n8XMV+HwDT4nOJzTRWo4K8p19GQ5TJxCAs4K8\nmVSS\r\n=X66m\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.44.0-30858.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- An implementation of `IFluidHTMLView` where users fill in the `render` function and the `IFluidHTMLView` interface is already fulfilled without any further code. The React view used in the `render` function should pass in the synced data object for the view to use.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n\n    // Renders the React view\n    public render(div: HTMLElement) {\n        ReactDOM.render(\n            <div>\n                <LikesAndCommentsView\n                    syncedDataObject={this}\n                />\n            </div>,\n            div,\n        );\n        return div;\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.13","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.44.0-30858","@fluidframework/counter":"0.44.0-30858","@fluidframework/aqueduct":"0.44.0-30858","@fluidframework/sequence":"0.44.0-30858","@fluidframework/core-interfaces":"^0.39.5","@fluidframework/view-interfaces":"0.44.0-30858","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.44.0-30858","@fluidframework/datastore-definitions":"0.44.0-30858"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.44.0-30858_1626991882705_0.19891189228984385","host":"s3://npm-registry-packages"}},"0.44.0":{"name":"@fluid-experimental/react","version":"0.44.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.44.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"880e89f94f97392cc29c1f15b24ac4d129d539b5","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.44.0.tgz","fileCount":259,"integrity":"sha512-eWiKJuMPqDMpnAU1BUQOBWNR3GQ+UzJlXXMk716mj3KiJF0qnaQEdAQXez2132bj6b19756MU05Rf0OI3/vxDQ==","signatures":[{"sig":"MEYCIQCBcKp9YZL0sV97MoUlRY6+nMVnHC71GubDDP6AXeBHdAIhALbSCU/BXoPpKQBONw3ZS+yiV04nefhk4KixgM2mNZ3A","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747565,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+hxCCRA9TVsSAnZWagAAKzYP/16YDf96iNrdqxc0PNfI\neRneSgzyy6t/LyLPFudHIBNhOaU/0fPFQUBlqmzgx0g1xUCcGpOTjeCZlXkm\npOUr0DwqhZZcLgpV6UFr5/iaMfCT9nxKtNrWUep06Ru/Q1Xbbj7wvWcLvr2d\nSPhvZfi8/uWkMDlwmKicB/bXEkKBmA8Rcn/zvkVBNEhsw9lEXLClzIEj4sIN\nTEnjbisypANt3+YD4KHXVydvEsbWft38CGASGcY9n9Rc/Iubx3E46VN1QVpi\nQprOG2uYwFVXefOQ0KWr1LqS8yuL907THw1oUXCIUcnTq0ORSUYpB8oo/QUJ\ntmfpC52oJXl1YFOIFFSBzbt0/v1dCpfZWlK2dOZg/2qvafAUM4rC+vXpN1j+\nQfwqYDB/XAR/yw07KoYBN4AM1W0xP+jfKpXrYn27ry2yEAiX/p92OOvkaIFt\nkh3DEFN/dZ79wRGGUty3hUYDclng6DXL/r+znlKRw5gQ794ZR0uSzsYnEBTW\n+r9ZrMtNjP3/Mb4LMAsHw+QFuIobDHbXsNPxRO9yWFd07Rqh3KIfOohhBxIP\nNRCFKNM2NbZEg4R0kTNQd5P+csHK2kJeggvYmx3VCSmg8C07RnRx794yJ2fP\neJdxJGbEjAuOG9U87uKd3Q9kI0KBi+m2MoXXx3xZ8a51x8DXWYAUHlAD6OEx\n9SJw\r\n=h9p5\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.44.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.13","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.44.0","@fluidframework/counter":"^0.44.0","@fluidframework/aqueduct":"^0.44.0","@fluidframework/sequence":"^0.44.0","@fluidframework/core-interfaces":"^0.39.5","@fluidframework/view-interfaces":"^0.44.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.44.0","@fluidframework/datastore-definitions":"^0.44.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.44.0_1627003970617_0.19264928651485702","host":"s3://npm-registry-packages"}},"0.44.1":{"name":"@fluid-experimental/react","version":"0.44.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.44.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"d9df47339809fe945a753a87ee4391ec2ddbdd71","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.44.1.tgz","fileCount":259,"integrity":"sha512-li9DatVinRf0y5WZq5g/Pu6dM0o/I5a/wzQJI10vAhTGz5WeTJ/x39hICiIMTlkie4xtjI7QxD6eytgGQgQNuA==","signatures":[{"sig":"MEQCIB6H+ieWkhTPjijupc0Rpdo2zUUdTOleWFyL16tF2O/qAiATrLkxzgYznMfTUlO4L7m2HK7rVenGezyEfkh8wtm0kg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":747565,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhCeUvCRA9TVsSAnZWagAAllMQAI4VhLXr7NGxyBAbKWYp\n9qPkMkaW87pPHPWjHY14a2NwfcVs4hiuPnRrlqzpJMig3XCRg+o5IuSP4vt+\npB/Kjgiyd4A7mKNFZEJYbmT40ViGuA+08vSTJ9rsndeTPaRSHdwFUMFH5EtU\ntZkjYNnsymH9vTEmNKifFbKlP1x/sttTekTcDN532f6yg7pnFGLFiRjsLhFx\nW/2hKJssZa+Tzse4zH3IJTAvz23MurTslXlRvMcM1iumyTYCozUQbdsghSne\nHkskadxUR1j4ZKjftfUoG+kCrNo0PJX7rcJ8fpWIBjqfmgZ+A34thqrcjSPv\nlTcqD/gnSDjgrgafpP2CdwecZtg2rpOjIW3pDNAwlxvRXYxLVqOk7fBOVuQO\ngTGJRl/fTkwKU0o3lnPLlROQm6gFZGHqyPHBOoEcWfyog6kcnRdZIA13Yvpc\nb+lB5dwfqEggmzMG2FBkNEHkUBjyAOV5HXtI0fpRn/qGLXKfeEq2Tk6I/gPK\n3OB0t/swIs5QuTRXHQ5t3wTvRnpMma+IXbEQWyDHR1910c7jH36ivbTYAARF\nYSPKvnaXEFWiIPxYsSyF5ueR+xis24sNK7jmc1N2EuDbG0pMOuLTtLjYiR9J\n+O4J8fuuImnpBNz6EiUpmG6mYfA9YuvDP1BPyaVZ/UzsF3Ufddtw/AG18tAB\nbSPj\r\n=j5FS\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.44.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.14","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.4","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.44.1","@fluidframework/counter":"^0.44.1","@fluidframework/aqueduct":"^0.44.1","@fluidframework/sequence":"^0.44.1","@fluidframework/core-interfaces":"^0.39.5","@fluidframework/view-interfaces":"^0.44.1","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.44.1","@fluidframework/datastore-definitions":"^0.44.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.44.1_1628038447284_0.8416508035473311","host":"s3://npm-registry-packages"}},"0.45.0-32948":{"name":"@fluid-experimental/react","version":"0.45.0-32948","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.45.0-32948","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"dfc02598bf9a0261a04c89305c161a1eca09db0f","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.45.0-32948.tgz","fileCount":259,"integrity":"sha512-J9XiDHQbcSALLQJ+ohUZd6OEUcSG8dUepif4AAH66ITWY7MmGFgw4c2kdflR3XhYuxBqBatS9zkY9fNhM3wmwA==","signatures":[{"sig":"MEYCIQCMvQYMlz6/aVwV97Ecb+WjI+GJ0YhGzPRCAmgv+X2T+AIhAORPALjItED2oBPgagwb3/i27SJJ/kNkqnBWvhQCTd+L","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742987,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDZA9CRA9TVsSAnZWagAA0VAP/RxvFQUWO4aC8HrPy0CB\nYCdWRCOyFmfGgk0fFjOGaGnlQvWryF7sXEEB4n8vFWWer8fjVGLlYZV61nsf\nwt918I9NiNlKxre12MGSsdZNVakTEVbHl5c6xmqQJALTQo6QAK0oLVeuJXkS\n6BIDM+yLrTw02R27lXuNYlcEyBgLi/5loGB0MNKqJ6jQJ6+PqE75qniChjSZ\nuqAsFLHrwBmCTaqAE/FbK3h3RaXMR+2ZRvz0P03DH2UpO8fLgvCyERVtb8ix\n+7Z2n5e2mAA0wFUZqZnM9N0goHPeS7IjfMiSRAWvA6QkH22EhtVJ/B4Tgn5U\nACJJZgedyRgCsGmBiZRqqhcHSPWP04cLOB8Wr62cd/GYBwqdkVIJmdH5I7jx\nnS6uTkLVnApJPnBTRwyoi5sFk8+qNRWPKbM9sFaTcxgo/pD2pM6zZ89GdfjE\nctPX1YNQ6FhTt/JjkImuRIueH7ylVBPY6EPJReD51Rv2Xp66jUzNTpaIedV1\nZgzslZnaP3Svrq7GtDWutOIGBUfIOgSnxFCtucJTFg0/Iv1It1kBDobXWaLU\nOJIyHG0Sk3YRmGs+WAta0K5r3pXVXMRvZ0L5da7vFU0VLxQ8y4incpAO27Wi\nXpUD3LQCmGhfGT5f4PzCyH6U/jILed0KZVN4YL7sq9w2iST9KihkkjC5FmkQ\nT3I9\r\n=kSX0\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.45.0-32948.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.14","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.4","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.45.0-32948","@fluidframework/counter":"0.45.0-32948","@fluidframework/aqueduct":"0.45.0-32948","@fluidframework/sequence":"0.45.0-32948","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.45.0-32948","@fluidframework/datastore-definitions":"0.45.0-32948"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.45.0-32948_1628278845654_0.9959380702032039","host":"s3://npm-registry-packages"}},"0.45.0":{"name":"@fluid-experimental/react","version":"0.45.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.45.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"3cada61509161a9852de360a4e769c8f452af800","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.45.0.tgz","fileCount":259,"integrity":"sha512-x5J59xP8O39QyWgZzW8XvvfJmoOU0EgMhWbCsL4kDJusTHleOAi79QyB8JhjAd2swcCT+w9y9CQGd3b+PpqvqA==","signatures":[{"sig":"MEUCIQDyjiE/W2vby3AX678saa+bpVhGSua5+pPf5m5+aempoQIgR+Qrw/woylNyttPb8VWsJjoUWGLTsO0GY4hgPnB5734=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDcQjCRA9TVsSAnZWagAAqZAQAKGYDX4ngD3PXnsqVnBS\naYrwzf3PLTOoXYIyk8aoFQQG3BtKqJfyxPuhJuAKds1GUVmCvPW54izamXEd\nNSmKYVCuK3EkSS6OWAUg2NZFcrUE+9mkq7D2bmxCutfOmf02a/WvbUKsrQUm\nmAkKjyZwEOoNWoknZC/BCXR7wiAZqKAgmfhXJYXZ4lbMESQNtLDp1xcAZGXe\nJs4K8B0O+6z4FVHrMablCs/8Hjf8s1rT6o8DBtc/9LeFZ1LxmI8z9TSxEcfn\naTqvEbhaHAJCnov+JBGXbAUd2f514bK9f7++xDUxs6v4bw5NPRxRAYXcMHQl\n0TrwIuEh1xcTJD7Dqt1ntsx9bvsLWUbvg13JXNZPpZnwQc4ndFBd3uZzEgiA\nyW1EH8fOhFPq9G7iDb8+J3ipi/xcgPRD4eEUH51R7vkM15Teatu9VnhOy0Cs\n6vbVZx2pkajsCb2IeEMbFk4cL+LGIpMuE0ysVMAESXq9gT8pkgMcBCkAmaM6\n+AtPE9+3syWAiGwazIXUbbCDaQkOv2VO4ohmQZFv9SeSGlSksPNv92qZDbt8\n6Pqcs+Kw45mWb7idaqUmGI2SmigslCC0J2/E/tnpCRlaZAtssoTOxyexfuqc\nT/BFw/Dchzo29jOjnFqLuuJjW3jqF8zpyc7lk12rQ/TIRS1XMEuu7k+3AHPb\n0WFN\r\n=5ZGi\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.45.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.14","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.4","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.45.0","@fluidframework/counter":"^0.45.0","@fluidframework/aqueduct":"^0.45.0","@fluidframework/sequence":"^0.45.0","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.45.0","@fluidframework/datastore-definitions":"^0.45.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.45.0_1628292130811_0.184595378568166","host":"s3://npm-registry-packages"}},"0.45.1":{"name":"@fluid-experimental/react","version":"0.45.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.45.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"d37c04a29459a3c1ccf63b4e6bae8783777cc78b","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.45.1.tgz","fileCount":259,"integrity":"sha512-BWRof5wZZMsIZb/ug3eBRfE4DTvuWpuCf2VFc1IQ6133JOP1h8jQbMNU1hYCr2C2d/oK5l2u1GjZVRdsheH6bQ==","signatures":[{"sig":"MEUCIQCqYjRR9KGcNmkh1f0k0ZLgejMVtjFjp7aaeOJhzYs6bQIgB0mP0nDdtTZj2B/ZnTlIvuGaTPp5OeWC6/SkgFDIJ2U=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhEdV4CRA9TVsSAnZWagAA/0QQAI/5pAKNIwLqf5XLU2OR\nBSj5rQBBET99mQ1PAApfHev6oh7CaUFRFkpdh1liGzt4i6L1r7siyahgOe2r\nfmHKRE7IQapdJkZyMmsSQt4jDRrWRe6VYv1Ld7TRWxZJrt3BEBWW/E88FV2J\nBqHM/VsbFwXoGz0Zyh8+DGecPF8eh/nGwmyXQQAfJuJbKWmVmI99J+pt+CLw\nLunZ/poHzPf/EoFcZfgMj1DTEDJUSYMLRI/kYdv09ecVpmwl+bT2muj1azCY\nr47t5cgKUMQFz6YVBJFsaELESkji1DhFhuR3XO2sM0daf4eNupz2Pf9HLQAU\nmKFIJYsW7tbc3EjQXGw08KLwFEKZrLfOn3KAf9NfLjQkqfaHo2Yz0WMA8pcM\ndISKuzHf0+9x3IaoYzMfeZ6dqkkH8M6bHphmqWGO56NV+xvHMM2j0aB4radJ\nRfkoz0GyAnOcKzB+CdrbSlEUoHT6sY+ea3gPCGeQY1mCdAEuEoqzLjuUrkMg\nB6ItpIY+6Zxa5HQc353GD7z6Y/HKpM2K5kF59y7zJTXOeYlYkR0MVU1huSn3\n5ZJv/aNGhTnTamKauAchPc9roguUcTPmTg/A0j3kUgmTLg0ER/ooGUM3+Vrl\niDBn10LAZwsdxcGH9Jb4wjgrbjF0Fh+/7RufPaamRZNUzdE5cHMhfe8ZY/tF\nrtav\r\n=OY8M\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.45.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.14","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.4","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.45.1","@fluidframework/counter":"^0.45.1","@fluidframework/aqueduct":"^0.45.1","@fluidframework/sequence":"^0.45.1","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.45.1","@fluidframework/datastore-definitions":"^0.45.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.45.1_1628558712790_0.9553877536810103","host":"s3://npm-registry-packages"}},"0.45.2":{"name":"@fluid-experimental/react","version":"0.45.2","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.45.2","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"43e6fcdf9db9d05b1890963ccdb5769a21fefc0e","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.45.2.tgz","fileCount":259,"integrity":"sha512-PR8FjYH0qwrZBG9FFPqocXFvFttJEININxiVSgog2rAtYNnUf+0WbzQOcS9/iGup6XaDGGEXjhAP3XUxSdd5aA==","signatures":[{"sig":"MEUCIHG5HPddawlVBGi4/dabo6to9TZ4Nc/tgi1nz7gdMRA7AiEA9HBnPvPqXbIF2ItJrchICEuf+Lr3RslE4nfGYJ7Qico=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhFEjHCRA9TVsSAnZWagAA1hEP/1ae6g0gdlkAv3jUmXSb\nD6kJ4PLuHOBe2iS37Idr7UBwSmG59RXCZMJbWH8W4ikcXcnLi3rSTuOL8lWc\nQaeTBe932PAaN24Bcj1BAHnRP9SSf5CtD49S56nHHW0emlsWgiHewMKszx3a\nMSSGrpYCPRM8G+yZDkej4wWnEmqItyMiH/+qoFBs+LI6QdulMXsFUGv+yK5H\nadkT4nBXCfub5pNn32Xabt0kM+/3TrHMnBmVkzH9klRcOjoISlIQHiWHVqgu\n49cn1FEV2b427aoCrVAb12lZ+5//qQsMMA0YNkky5ltBugi8Dwc/TVN5sgGe\nD11POF18o9NSOcx37t8WZWlQvNtKWbd8/jozlIGFyqdGCYy34ksAdOeZvjTO\nOf34xvpt5ivj0Ue6DB9V5FBVCSTT2lZ/2gUAq+wTzoVxWfr7P1B6UKH42cyC\npJE3Me68I+lv1cGm1RquKWTp5XyuzODYNSd4PZl6Wt0+WWnkpNnBCxLvxk67\nyjohvgJFP1oaBv+lK01Gk901NOBvQk2/mPvUETvPzOHHz4OEyBToX4e9foIS\nXzGbNtC97iqAtyDq1PEGOD9gBSaa1T/P5ws7nge0Ocpul7EzYsfokzj62Lr6\nqCii8M9zYYlwYU/0q4eLoSseWdLF8UaALokGsT1q5VB8NGiXO5eGLfpOYuhx\ndZ/N\r\n=jDvO\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.45.2.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.14","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.5","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.45.2","@fluidframework/counter":"^0.45.2","@fluidframework/aqueduct":"^0.45.2","@fluidframework/sequence":"^0.45.2","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.45.2","@fluidframework/datastore-definitions":"^0.45.2"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.45.2_1628719303008_0.13832846091492845","host":"s3://npm-registry-packages"}},"0.45.3":{"name":"@fluid-experimental/react","version":"0.45.3","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.45.3","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"329a86c9e921349a9764aead1af4ec4312dbb0bf","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.45.3.tgz","fileCount":259,"integrity":"sha512-Tn359d9EFbC69AIK8j1Tvk8bcWpiTj4zG+iUAmnpTsRb6Hh6UpBb0GdYf9mDtVZkSUFKmy+NXGcG8YdffztliQ==","signatures":[{"sig":"MEUCIQCXjYeh4xiS6zfPCW3nNfSuzeyFtj2Hjne40ZBCDcyCfAIgC/srcBLv3H4Kg1bPVRU5uquJaIXhQ/AAJlYkIq1K3XE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhFcHLCRA9TVsSAnZWagAAJT4QAIfeQcP0vxRf0NYKijFe\n6sfxoxMBKnJvGPm7uqIxy6B053qF4uBmg1WSsMNsP0DCpsXH0gmRtCyuhkXR\nvDfNB/0TWm22M2f/y3zRooWQ4XGL53DEDYxy8g5np4jlFSb/7COT/eOHKWwn\nXckgqa31aqvR/v9j9rG7CbjPhsCDQMLiombkQln5HcihNjA8MhSqyEZWKceu\n+1Qi0eRZGXNbPQpY19SYzGNtosE9+NfwBCXrwWzrfrE5N2ny2ziCTi2CEWFX\nrG9EUdukclUhOOuP8nfQP7Pao3OZSumJJQ2uYSqAkuLxdctMqa96Hbz/yHQ5\nvzZodThENeB5ypPoLV0NC+zmkwEWNIFme4dJy4L+0yTZWhyWdsDKr7HWm5mY\no2tRMqt5QdUjtp4J+J7WdGFCyJ4F2CyrL20OKhDUfMlAJYHMGcwzO5MF2Gs0\nVsXRN6dgzjn18GKSjYHRbIcLilL4gfIMLhtqdwcqAykETBXdOFAxlacyDM36\npQYaz/JnAviUZxo69tA0iwbudp2Wt0SB5OfGCbby6Ns+pviT+WHy25OuBOFP\neeXDQUxEbLIoiq5NbFUoGFc9JIeh2ZHHO7x/ScHO2LPE0INKbSvfD7UgM4ey\nnaNHzSoc3HtdrDTeVz8RVCeWpL8GZ6gu9eybq//cKFY8Mk8JAhPEKdVsU+m0\nMhYh\r\n=r17h\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.45.3.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.14","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.5","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.45.3","@fluidframework/counter":"^0.45.3","@fluidframework/aqueduct":"^0.45.3","@fluidframework/sequence":"^0.45.3","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.45.3","@fluidframework/datastore-definitions":"^0.45.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.45.3_1628815819050_0.23068976582127854","host":"s3://npm-registry-packages"}},"0.45.4":{"name":"@fluid-experimental/react","version":"0.45.4","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.45.4","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"f7ae1842fa7d4530112d1fc790f7aaee9829c6d2","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.45.4.tgz","fileCount":259,"integrity":"sha512-vKvQFeZfBNJ0D1aorFkq2+SXXx+tLLLK7d0Lkje+rT10cZlaYrHoPCze6WOroedTMTbQf976QRK/tzlgLVHYNw==","signatures":[{"sig":"MEUCIQCZkR2vWacd3yCaMV/DgJ/ciO92lmWwfDx2uqEHxvDzuwIgeVir2ktyvFiZx+JLHHitOfKZgR9ob568vYsNxEu6bzM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHADQCRA9TVsSAnZWagAAoDQQAJVfFtxO8dwnLrnCQZZV\nLps3m+7NCrYXwTdp7kbDGv7aJ223lUTDmqJek9Y8uVmJFy+NzCrq3JKwx9YR\nNpADnZQc2+qtZ0fcNlJEa9jVhROHQkV4cwrICBVUuRTWKn6b31QdoGreE7uT\nuSjLh9UtbIoXrvfmKLr6DgolU63HCXkKNJJKdPL3NIICbV51WbrOESJVwzSP\nGmjA+U9omhRy9MFnLmH6BZaqPMTeRD6+7+Xv1ecREfwkLnqIc/Wr1aRfpkS7\nGzGyx02JM/Wi9Rn8OiYfj8pUwJ8xLKr0Mpd4M2DAcXnjJR9U4wdtvmqqkR7x\nK21Fj2L2WCpqLTLgW441s+ztG1Lt1v2u6yeUPLXYounr/eU08279YpUBv/YU\n1lubFLsccvGa72HgqiYFhZVq1UpcN9wuV23X4WUoIFA8wSO3zb32ZatTAO3y\nocEDdQhzvTANH26vot4mjJLs553S4Mq4t945LTVLFxnNIu6YsWJbDbDGXnBc\nnlHGVzidVFYJawZzjZFrjGP3Z3fKUrMnGzeOzNprNr/cWfaEOtR4MtpMmUyJ\n0zTOFXKfflUaiyBIPjurelcJNW4uh5jyNYi38GSustSwtPbh0VoNvoDb4tP3\nfVAWMIrlp/zg7F8UqcyHt1pvPZb/Sw24UehRrgv6SKMRbKNCA+2e8EW0rK7Q\nbXXD\r\n=uCtw\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.45.4.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.14","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.5","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.45.4","@fluidframework/counter":"^0.45.4","@fluidframework/aqueduct":"^0.45.4","@fluidframework/sequence":"^0.45.4","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.45.4","@fluidframework/datastore-definitions":"^0.45.4"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.45.4_1629225168820_0.1173707082795028","host":"s3://npm-registry-packages"}},"0.46.0-34784":{"name":"@fluid-experimental/react","version":"0.46.0-34784","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.46.0-34784","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"67334bb0b3d34aadb7bd6df5aec0cdda65daa8ec","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.46.0-34784.tgz","fileCount":259,"integrity":"sha512-yRwilRmHQaGHYBYElsaYZ+yiadQuLWRqVft7UjDGnkTDspjViQPSOTGyqItO1P7ItBNrVOyQs5MyKTzjo789/w==","signatures":[{"sig":"MEYCIQCVTQupjGYh8INg21pkMKboS94EwClhOd/xOQvVfEIxYQIhAJvfKx5lXO59SIT49KzCBhO75UK2kVyNUprjQ1gXKSm6","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742987,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhIDZyCRA9TVsSAnZWagAAPQwP/RIzhRIu/V4czPzzhksL\nf0s3L4ElbBWJCJrsuNUc5Y+2IuGiBmJkdaJpaZl7toKfug1oZy6Z7n4qlXaL\nyrIEneExcpqVUQ/DfTVUFSAoZLC6H4oqo//B3Qt3cmsYW0TijYrj7eca38oX\nIsIyqF5PSPDcB5n6PsVjKG5LmfTZNeyEgeUvpM8n5XfqHOCoc6rCVnPBKETY\njKC9IUQUeLEo/AzybhIH+cqmmkLpTGe3ELfPAc0nOR/RdUaQzrQL7K70F6R3\njOcSvAbjsLBf4IkAH2+XJw6vn1JFzVfC0n3z411KMLST2vez/b714qEG3hg0\nNd958ihXmq4SvVwUt2AzaRsDAXKKx7N94U0GfGd17Cl317nPc4/h9GvGLxI2\niTgOzoFkci+yGwBVQuuV0BYgsAtZHXL0whFU2HGEzUyQdm+EV4Bmn1EAnIH+\nSK03ypK53Vavb1Nwq777WeHwm8orXTg2AtSo2FfE2T9bed0EHLHswwwsPpKx\nh0nMi/I8UTC/kQdl1W6QA2rnl6upkWGMa36vigcwodDWbuyYZjn3OKn9e8Nu\ndcYLNw4WjQ1HJ45JTqbAJhnNe9gmtCzAJz4s0K1YURXWXWmbE0pwXBK3fRNb\n/2f1fqacWWnFwf+IiYnC036YhUQsIaZ36+7yJJPaWUwFxmdyaN0tBCyJ5qUq\nARV6\r\n=uQ66\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.46.0-34784.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.14","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.5","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.46.0-34784","@fluidframework/counter":"0.46.0-34784","@fluidframework/aqueduct":"0.46.0-34784","@fluidframework/sequence":"0.46.0-34784","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.46.0-34784","@fluidframework/datastore-definitions":"0.46.0-34784"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.46.0-34784_1629501041959_0.4300902830893487","host":"s3://npm-registry-packages"}},"0.46.0":{"name":"@fluid-experimental/react","version":"0.46.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.46.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"97a70e85bcd29fcc807f1f01f567815577dec157","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.46.0.tgz","fileCount":259,"integrity":"sha512-pOkqnG5J7/EE9x8DCQZGOOminyPvT+yDbnNuMyFNv1l7Sq27dMs6Me3pdAIZq6mNf4iy+mHSGVf6M6BCO4iX4Q==","signatures":[{"sig":"MEUCICgG77AoMZVuB3/gvjQrlOqDaLjzeinDf2aYs5mfC+RHAiEAtOL2U9HJgGXFg9dLk+6xlh08iC4bKBkqlq15IuzuxEE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhIEzWCRA9TVsSAnZWagAAgmwP/3b17W/ycudzuC9B3Dfx\nFNyshlOvXq9+m4ybsxsqm0F+78D8yZthCSd3cwaDplhHUoivDIo4eJuCkdu0\nc/O5oKaA/REGpcFTs1EzhJHvsRWGHQbr2EHuTlYQUpOeIh+fE6rORR6dp0W7\nnyTBVS1V+avv87FMuas1vQG8g+bqT/XQYXVkmYLjT98szL94qg+L+m8gLT7N\nZ8z4bxWixr0i4z1r44CJyVdx1B1scKvwUtUdU36fcBrAAJne8w6WqE50PJKS\nWJM8ScL3AR2gJy0j5Z7o3f4BMwKLTKJu/FNBcsSxbLV2zmD7VCdSYr+e5P5F\nXC//x65ri6Tk9dmWROgtd4opaKJ9LcuUAopcXUx7OL5KZ5uFNgfCJ9cpl6ry\nxclejrTLTBgzzVTZI5y7XucB4F2SY0N951czCmh4faUg+EdP/gwCYvqb7QwU\naYx6aafjf8ng3YC9SXf958gaPryoDuRq7O1LIJELvwk6Sw+Vd+04+1P6nwsX\nn3w8vncObRSa4UdXuuD6llzM5z5JTWFTekAa+eRxx4FPrhqCL4fifwmPYKvy\nv8pkysqRrh7J8a9/Vb3YSq6aw4fy2EDnsCn40GoQGGAbPkLhPipwplk6Yx5F\ny5a37ty9TW66SqOaV8LhaqT3IPY+xz8TZAdoQLB4oG4XreL9iz6tHLgD/CmM\nJ6Pd\r\n=EBzr\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.46.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.14","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.5","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.46.0","@fluidframework/counter":"^0.46.0","@fluidframework/aqueduct":"^0.46.0","@fluidframework/sequence":"^0.46.0","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.46.0","@fluidframework/datastore-definitions":"^0.46.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.46.0_1629506774359_0.1631296375138218","host":"s3://npm-registry-packages"}},"0.47.0-35912":{"name":"@fluid-experimental/react","version":"0.47.0-35912","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.47.0-35912","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"3834d2b03b8b41f9f382c913a9441527d415ecb7","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.47.0-35912.tgz","fileCount":259,"integrity":"sha512-i0ZZ84EPIRWPO6OBKfUfN8o1sPxcRzFwfZYAHWR0bUMm+FPRNBZ2VhzYnzV4kRFEL5hw1/zD5eCR8Ao+E99+QA==","signatures":[{"sig":"MEUCIQDKqkBsrEIVysoO/am8V+zATXAYdsnUQPf5GueiQgcfPwIgAO2zYnZvkUGXmLBMcFlagWXikCpLgFzT948giVMyhqE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742987,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhLsEXCRA9TVsSAnZWagAAcqUP/0WhwIq15W+sIKysR1Vo\nxb6kji63fJa/9PlWwADTwTpU1RwK2aU+dTqsoIWtgT2Dz6klAN+mVro7Fnww\nvzgKV/cjkPfT38eMusWFBdICwPqPYMgKj0N5g2x4CdXiZxVolIaWnwqPiJYS\nsBiDfe6fcvrEaiq4YlIKneshR+Gt8W+iU9+55xRzRNIf9gvf6+5j5pmy1lvA\nutTcKhuEfe5Sl9DqFDxszn8+sttqx1yii9JfTQxRkMd3M9EnbjJkDlu8c2oX\nd8HMTyg1Yk6AnWTtkla4gjcPh3azHEs99xT1SaJsQ0Lcgw4tFU9JTjMyVdqC\nMfiVfnbaJm8toLmt9IZjDdlNPncP0yTN8S2K6LzCU8icZw+DrCY5DszDSZnZ\nw5NU/a0Xbr7zd72W0X4lRj1ZTJqlUP7XApZsFV4rnJUvjV3UdIsjI/PmzknR\n3vsMrfUC8VbhF8chz3cu1TZ2ItzMRMal9tWp7O5y1/JDh5RsNRS9Zw6NggDS\nNtW3hqHe7sNuL7YpmQQrS6/NB4qhaPzfFiV3pNbtG/HMbENcDAdqkqocELUR\nl7O5bSNByIflamI9MvUyts9bMlX/f9blsaadkgIK+ss6LLYPOaswU+irD62q\nidDc07IJNiuWz1GyuGmstFFvN+Kt2Q/EDwPUx4cLiHF2wzcD2wnbNFi1MqdQ\nTSni\r\n=sHsN\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.47.0-35912.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.47.0-35912","@fluidframework/counter":"0.47.0-35912","@fluidframework/aqueduct":"0.47.0-35912","@fluidframework/sequence":"0.47.0-35912","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.47.0-35912","@fluidframework/datastore-definitions":"0.47.0-35912"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.47.0-35912_1630454038902_0.6199107369728936","host":"s3://npm-registry-packages"}},"0.47.0-35961":{"name":"@fluid-experimental/react","version":"0.47.0-35961","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.47.0-35961","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"e73dae7b2f7aa15948c58643a146435c6ba741d9","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.47.0-35961.tgz","fileCount":259,"integrity":"sha512-Nag7nqB0OqfzQGx62EpVK9BkYKJ6Hn+znL+DYcsTznsazlTDm4+jH3F5iM7+moCA3xA4td7WqSajKyIY8s5Nkg==","signatures":[{"sig":"MEQCIAs7Qq2Yb9Hpi5cyhzr7+a0zBsCEJKc8uhB0htBe1ozjAiB4oK78BVykvcJ9HoB05y0EWf3cC/7KuynMC5c3A3h4mA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742987,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhL7gVCRA9TVsSAnZWagAAR5sP/0e66WCMJzV7yJ92W2yR\nZRrMuenSqZu2VkIROEsD80atDcHjOJSnB47/8m3y0jvfMsGCFfDKPQf5heLv\n0eC9j4OFXZtlaQqPTU9uu5iknjTKUl9W8Os91jz5kGGIWwGrhN4k1LQb59Ds\n5fYlAcW+Rpzh4cPRY485CdSZPlAddaLcQklpa6HmLTYhnPXu2ofahj1x3B9B\nX8ktVkHYZxOqB/4mMH2TIoQolqCkd8g5t4EtMtBoVIEZB8S531v0D87lnvhP\nBVwJiyzAtXS5hAu1Yxy/9E18n+kJDwOUiz+8jnf2TNTFXaLwGhZaLZmjR0hI\n0hq60zs8Mz1PTXy8sGZAfOahYOs1voGnTloN4XTcmVQux1rzRauHC4okDVPh\nQOLe1sPi37G390NtD6ADVazGGbtmdNgvgIFccec6J4hHyrUyzycktlzpYoOF\nX/QVJouiBKPG7nEBoWJGp6v5PoTBhZ4N2pmgo625ZBx3liBHVDbLofJ6trIB\nbs0+twFbJwQzW3AAIFvqGZVzGDSilM5mE+neS1QPiuINzzwHGNJq31HmedD/\n6eAWNpYCWUbQbgoPfmepmcmeeHRLcbawN0GvJDW+Q3HNLq0ZWe7LVMpXBHUK\nsicaOigxRG+rEU+z3nYkJ04WJrIubajsQUqji25fcgPI8qaK20PQhf21GVqO\n+rbO\r\n=1rAf\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.47.0-35961.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.47.0-35961","@fluidframework/counter":"0.47.0-35961","@fluidframework/aqueduct":"0.47.0-35961","@fluidframework/sequence":"0.47.0-35961","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.47.0-35961","@fluidframework/datastore-definitions":"0.47.0-35961"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.47.0-35961_1630517268871_0.4025309910002075","host":"s3://npm-registry-packages"}},"0.46.1":{"name":"@fluid-experimental/react","version":"0.46.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.46.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"953328d1066d4857655a3a2fa34f5ce7886218d1","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.46.1.tgz","fileCount":259,"integrity":"sha512-WhqJZGKFo50O9cKNo7aNm0+f2ywfEDPsw6H3l+SwsWNhiIbzcugRLt2BtOY8wOlR7H0FHmLsno6DgdxGXeUcCw==","signatures":[{"sig":"MEUCICZBLFwfo0Pi16wKBh0AaKw8ytQgCxM2y/tA+x39VPdvAiEAz7viuHJPoF1xq8itb0lTb2yo3n2KvS1qTJFg9ha6aPM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMVVNCRA9TVsSAnZWagAA1jUP/38zagzhGoBOKuLpS1tT\nGmwshnRXHenrHV4dsikpviOthDA0vCLrs4sD8CAACNpiYeJiDL+scSGLM4Ny\nK824/3yBnGvreyR+Rxx9Xv9DcSuvOKqJ2J5Q6JFbdnl9p+e9BQt+uLO6KbfS\nsnVT4pB9aMlE4VUYZ1ij/6bE6sN5ZDSvvcpSNp4SbTF1FkM4i+GnitILn4jj\nB0ErylhRtA0dOwKHZsqjgVaaxV06ph7iqlzFQJ1dZOXiHjhtCz9l1drRbEm+\n4DgzgVnMurkwr9rRdX2ppuVAC/LwEGXcL9hkAAEJUCHfr46oAoVFjScLKicy\nqaWBT4takSahUs4eGGU+aBSh9YcJt0zk6RqINfgrZu7rDfLJBKrqj5yWzOIs\npqRuyDdhY3sYkry7VXd6eyN4MxHlGDyp0sciM1Wi0Wm1mkS41UdPEVNhJf04\n5D+SSpTfuJxSJe1obwUcsg8rTVnP3sITKCTAKrsiBYoeCJwb6SEIPZKn1ZzH\n5oy6JB2sa/hFCU0Lpu3lH7T2vh3cJ4eq9/QXlfkO6Zghxj4p8bJb6UIyvYTW\nq/5/rlsK8jssbBt1cPOALnmnpjXbOQcemfX42NcgNHtYcmFCM+B9BdOUymMK\ndfbwb3aidrgcdfXJpsCOO8t/jA0ihQOgsJeGO55IgHIN05WevEMIJ/hOL5GD\n8V6N\r\n=tmEE\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.46.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.46.1","@fluidframework/counter":"^0.46.1","@fluidframework/aqueduct":"^0.46.1","@fluidframework/sequence":"^0.46.1","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.46.1","@fluidframework/datastore-definitions":"^0.46.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.46.1_1630623052938_0.32035897616549835","host":"s3://npm-registry-packages"}},"0.47.0-36362":{"name":"@fluid-experimental/react","version":"0.47.0-36362","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.47.0-36362","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"1062bc1fb95892e87d45f0e4aa25d6008806f6d3","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.47.0-36362.tgz","fileCount":259,"integrity":"sha512-/yNrbFKXVvuwNR+Yl2MKOTxI8Vze8DQw36cIhXke9CbOeAit8zfv8x/gizxecd0KXp21RAjTuPHNRL4rHILv/Q==","signatures":[{"sig":"MEUCICKdVvNya540vX29XlZE/5xfTiOFXtm98F59vzQ9g6xdAiEA4N+hPKqzxt1fZhO6GhlUOsj2PSrwQmi+/jff4LnMnZI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742987,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMmooCRA9TVsSAnZWagAArVoP/jbrQquzGpFaLyZnDyX2\nEqdTe/DKnBMpE6w74Vvi65ZkIzQCcCyWORDRfd8ZN5An0/yqIwQqf01+OInH\nrzNlFFz28LrYYYC/cFbrk85R9FqRL0wyIW6AbHwAl2/OKk41NZ3FUqFxvUKI\nqDAdE8/Sy+p/WZuzP5JtxhsszkSNc3aTrtNNy7X8hIxEahHFSCcR6mU2WZxn\nYltU+WbzVvYuqNl/9pax4cVMZw2DjAc0xsLRJXv2lnpw/ZQtNAyKLLY/1EQz\nnhDlYkgk/8A/+OExzKwEA0Qnc/k+mPYFD/fEipozJ/G2aHfoZAzdn+ykBt43\nhpNbOtaVZIccFta0opbnBjt1e8rNNm8oMDh1cPaHfGDA0J6xuc1pweVK188P\nZS+dqgd9M+aKsGBwU0f4PS4aIMm7VQeWjXfaCeuR1ig/eC+HN6njUaTyxm/3\nMXrHKzSwS60DcA/PhXrQeho81Ifj+5Wo80qNX5lv/zZra7HOIL0zDSjm567b\n5W4oXsgIb+VweT5k3/uQZRd4e5NL82HpEZkMyyuLQdUhEBPB2zPjuDIixePL\nqjZy2NgQwUtlzWXLeWzFf+8G8VAJNL8IJUaJvVyfxP6O95CSZmoWBvjKhtlp\n+ilPzXtO1c769812bmKdBy31hYcLpR0VBS1CP9eBYEofTOfyvpKwUKmzmioc\nzllD\r\n=2d5m\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.47.0-36362.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.47.0-36362","@fluidframework/counter":"0.47.0-36362","@fluidframework/aqueduct":"0.47.0-36362","@fluidframework/sequence":"0.47.0-36362","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.47.0-36362","@fluidframework/datastore-definitions":"0.47.0-36362"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.47.0-36362_1630693928394_0.18264688062451162","host":"s3://npm-registry-packages"}},"0.47.0-36699":{"name":"@fluid-experimental/react","version":"0.47.0-36699","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.47.0-36699","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"44f938ce115caeded835be91e76432f834f6043e","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.47.0-36699.tgz","fileCount":259,"integrity":"sha512-oKzZ8PF6uCX42rWnr/X6iVVYMy7w50tiPDWqXWSEswulf3V5moOtuCGQBfP382iMSLwwHzUOkVzxeVHvdpBbkA==","signatures":[{"sig":"MEYCIQDza1IOVXqer6DFmumPafHDlVqAzRrCtC85JQ3elro1wQIhANk/rFrBytRxDC772ZRekzH0CKd//PgkN5KI+yFEPqBi","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742989,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhN/kGCRA9TVsSAnZWagAAYqIP/3BNSefrOOhy0VosOILV\nCyma66acgKZmVbiKANpL47oVtsZ8jKp/jN5mFbLvKp5+gyjz7wEdft64V9RS\nECtqnWR6+8XfU8mAI3gwKlpSZJvEERqe8p+H4T4dz6MJQ1QI1i24nZxBO8PK\n+aE8uEIqn7wVlMFHSW5XsVPNMI9ZeE4cf4f4S6+1Ynltzl8dNZQ+I1TcednF\ny2+IPNFehbQKePmvcut+V6oh19CMzK3ZLoZHgvlo2nPhYGsQmi53brnAVzua\n/veRabkX+0HW3X4kHbVlf3NDUtOQpsldNZowKSTAshmgSMa5JuFdVcbnyFbW\nNXxRDn5s9TRoaCjn7hqtJrB/0bbeCV+/+THGvjn0/ef+HNpLAM/5UUvo+M4s\nbUJHEgcFhHgkKpkF2sNxWasCASWcTYDW4GSjbNI9SSS17zjWzHX0A97lwGSq\njGno+MSqDOeVE9mmzoGsKnu1PP0kuIDuvcKV6WDSGFSpiUsT4hIsZwetePcj\nEKJ7OQO+lfe8U5goucGS+Ir2xk1j5jSI349Vc/V++aDdsbhKc+Ns8W+jq5Cn\n4S8KdNe8e0gw2QJNJs/EmDtpHKHKyEbUmgQsLIBS4m4a8jzj1+Ylr2s9LGNp\ncHiE9ieVtJfjCueSjpdbF2UP/xafI36BMLe16BbRyWmclA9NHaANwAEjUtu0\nJN8U\r\n=QTgF\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.47.0-36699.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.47.0-36699","@fluidframework/counter":"0.47.0-36699","@fluidframework/aqueduct":"0.47.0-36699","@fluidframework/sequence":"0.47.0-36699","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.47.0-36699","@fluidframework/datastore-definitions":"0.47.0-36699"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0-0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.47.0-36699_1631058181901_0.8831787064936376","host":"s3://npm-registry-packages"}},"0.47.0":{"name":"@fluid-experimental/react","version":"0.47.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.47.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"0235f3a6ccef1532213ede46ce96d73fc861342f","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.47.0.tgz","fileCount":259,"integrity":"sha512-HF9LD5Pmq6mY3CzExs3X7lxOUynQt6zqRIC0d5IDpWFRj4g4Sj2O+yZseS4nyqceWnZsBp5D3GOGB4S6ajNQAA==","signatures":[{"sig":"MEYCIQDWT0MtTIAxHoVeuLcsx+VfaAA4YW5dKe2grFj9clMm/AIhAMLjUqIgFNvbrljcMISC8fFBmRPuUcXulHqDtTaEO05c","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhOPo8CRA9TVsSAnZWagAAn9cQAJQQp6VqolESZT0HwBbH\nWZUz4Lchv26i7Vp5yclaYXpZyyTdelrxWDGKbkApXHoRNCD7V1d3lk1/gTib\nPEf82lld/RhR3cgJqgpZBDO6H0oBazws32wtrrrXYQBSDdQj0tpFK4OXZqqu\ny9X1GCnk6YnazdQx39UGNFfoa+h1eq+a2Z1GA8LfW8yObOjKTGHtzGMUDMpL\nJHOCl4eCTFaCiRba4voXHSuCY2FdLOTF8nLvsQA585oAy3feQ2idxcqSu561\nPoDQViDY/ruKjjBn7VbqvBHAEFy9lP4piHNfAb9tPrYnAgj/fLMq0MMLzsup\nwHr+anAKqZVdCmRJXbCd9MO2ErLqnHsBWsg60NNSE5rPc5+esWrsuyhDgh1D\nJunCHrCMTMYPjwy1NamAGVLgPG4XC1k6eyWZs9bCvlfXnkS90hgYYJvL93R0\n2uGaD538235Io67cu/IBr29hEYriK5c0k3yUzVkL7ZhUNxMqEjqtPyZtPM90\nb7BHVFV+XJb6P7g9Tgq8Vigs7loCii1dd5SyhK5iNO9L60VpzGYlQ6orE4FL\n3yDA2QBWKjdlBiB60GbfAu1RyGUG7UTAR9kgySw+g5tjhv/jRoz+OH4maFTY\nlnzDYs/3xzjc9Rduccxx3faJ7L/hxovA5gQVQn/MIFHXBYuG5cn5Y3Jx98Lm\nmdYv\r\n=4VUK\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.47.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.47.0","@fluidframework/counter":"^0.47.0","@fluidframework/aqueduct":"^0.47.0","@fluidframework/sequence":"^0.47.0","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.47.0","@fluidframework/datastore-definitions":"^0.47.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.47.0_1631124028111_0.7820172792338418","host":"s3://npm-registry-packages"}},"0.46.2":{"name":"@fluid-experimental/react","version":"0.46.2","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.46.2","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"0208eb297017640c3942decd872de8a77adc052e","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.46.2.tgz","fileCount":259,"integrity":"sha512-dYpUh36srXXzP1fFhFq5kGytN7uuGH8rBUMQ4cWz95AyASrANL7MatOOxXQQEk9qzliarcs70VTkICF4Ct+oAA==","signatures":[{"sig":"MEUCICWt4/cKqvg4+FelTxOpNNwuCyplJVtN5vkd+a3h/vepAiEAuc0Ocv+NUxWXFjwJ2UPwxcycopLy+0LYFPO5AO83XnA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhQm1bCRA9TVsSAnZWagAARIQP/0VI2VsiUS1C/i8fFHZ1\nTZlDOB8PgBJn6/ZMJbeesuO4S8CJnnFab3KTI5OxAkWQDxQDv3KdjgzeR1zb\nyE7zvQVU8FgbZyrsSSbMkC1CafLRG5pj1j0X1Thi1cFmy8HEEUO/DufobBv+\nD8lB/cIIKFIxJcWmZ6DcLcQ4JYSk79ZDMRa3sVbVL2x/mSKIUM3lKfxfBV8/\nUqCoz9qXOMAtHMEZChhRwk32xU1olm3LIVYIc4DNNaOEVekiHuJMR8S8nwO5\nybq8W2CrJHSPgpMQyiqkNOmkJI0QrsIbwJUalc/RIXh5J7t+dY+jJtjDX2gB\nStsIe+NAM3BD9g6032mUCYfvhOptdCZ03q60HS+QW2vJHVVoC3KC/nKXvZsY\nrhdalBhusVNnDjt8H1vCn9e58IuRtgnOVoa4JqB7H89ggSjtDsYky7OPfkW2\ndDIUaATywxEEXxESWG9MPcpj2UrqByT6MEq79tOsZk4ihpryKDatASrhBaLa\nSPFUbYLrIkqKM70E0pXrF1gY3D9MeLboMTNwgOKKTRbmJS0ok4kouXMwHnvm\ngLsLkPZF+UGqT80Ap5eqXw+w4Rja2DAO0LmCKmnDb6yA1WL631njReI7Q9Hk\nIIzyaEtZtS69YI6zqPpwCCP/qtXC4P6BRGCBpFHeK4o50QuOzTq2CGHfhJv9\nsI3T\r\n=0EdE\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.46.2.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.46.2","@fluidframework/counter":"^0.46.2","@fluidframework/aqueduct":"^0.46.2","@fluidframework/sequence":"^0.46.2","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.46.2","@fluidframework/datastore-definitions":"^0.46.2"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.22.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.46.2_1631743323682_0.3812024214121037","host":"s3://npm-registry-packages"}},"0.47.1":{"name":"@fluid-experimental/react","version":"0.47.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.47.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"443e40f2ed8c34ff0f964a89af12a41bef1bf542","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.47.1.tgz","fileCount":259,"integrity":"sha512-VWH6xcamzDsVAPszoIV6sTKRH/8Ddnk7ztXu6Aa8qNw4SGHN7ARgz3hHJ8jlec8wMvhkc7nlAm/Fa0lWXNwtdw==","signatures":[{"sig":"MEUCIAsODPo6MlrVOPN4DdvDbSxf3XqmSNunlketvfBUGQ1ZAiEAtrGjAqjMz4QYKjkueRkNls0NULgm55PndmP1wC2GJY8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhQnpkCRA9TVsSAnZWagAAV58QAIA+EweCpfnIOTBEPX30\nyfB+MfBc5WyRZWUvuLzsalBC/A+O2xeBAZjOZB1ncpNKuJlsmWoF7IkyGaPC\nc6AoiWx6jerYKXxZQz4Krmop8v9+Xum9MLTMxCVVNz+mmVfIrlrTMDUXVVLd\n3GC5Jnyjk2mP3OFswxA6VfIHg6B6Azo7xNgsPusZ/q4FL002vW6BMoRraudW\nVnXmETePA5BkPTHNu2pefScrjbo6AQgx5OebxC5wW9BXFPq5YMyQmmjlT/cg\ngD8NnED6DRCJHLt63ZSyCWR/515vmeGLq/i3EU+l82ThqdhBrUOHsB5pYFnZ\nAHAV/XjQOKJinEUTQ2tpZdsmT2DZKFpeHgLQaQ5L9yZWhBjNpZw3Dd3Fl8zS\nzwnEaATKMtNjN6BlPIr6DDf3ZcouIBn4tkpLPoYvZiou7/30+L8z3jy5E7vP\nK5t/yii8KLjT5l42ISJATq2S9MBXbIhzOZZJjlSjg3CqfZvfDXe//Vt/eIqS\nBEuKjIM/tKbC3V4J/sYvenw1vrUAwL7z+zaLaR0QFeZ2nOCUDsBUc0PVuRLX\n6iQ2Ty/QOmh70R/WcC6dnt0NdJ9QJFlOK19Nc/wr2Fvbg/e8Piltp7ecysI/\nGyj0O8+0uEC+w0R4brBwZd75Gn6p7Fhuo+4NN3LQUYzwHSdb14hJ0CF03jr1\npJy+\r\n=bQSs\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.47.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.47.1","@fluidframework/counter":"^0.47.1","@fluidframework/aqueduct":"^0.47.1","@fluidframework/sequence":"^0.47.1","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.47.1","@fluidframework/datastore-definitions":"^0.47.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.47.1_1631746660236_0.08523509948795405","host":"s3://npm-registry-packages"}},"0.48.0-38105":{"name":"@fluid-experimental/react","version":"0.48.0-38105","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.48.0-38105","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"86f55b4000d828d187cedfca0ab1cf7802165354","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.48.0-38105.tgz","fileCount":259,"integrity":"sha512-HaUhn2InL2zl3jl9/CpUiT3HaNx8/alenSLNq2mxANibVNJbVya6hIiWwehcGyg0vhk4/qJpqlhGmmymCdvfQQ==","signatures":[{"sig":"MEUCIQD9W6wK6LAfRW20KltHA1RN2QoUDQQ4U7pHlYAXZ/18KgIgTFfP3uH1a/pJg1PNOeYDXY+nqNxtMAz9oh7fnhtRqIE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742989},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.48.0-38105.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.48.0-38105","@fluidframework/counter":"0.48.0-38105","@fluidframework/aqueduct":"0.48.0-38105","@fluidframework/sequence":"0.48.0-38105","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.48.0-38105","@fluidframework/datastore-definitions":"0.48.0-38105"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0-0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.48.0-38105_1632172972019_0.02801463487382372","host":"s3://npm-registry-packages"}},"0.48.0-38142":{"name":"@fluid-experimental/react","version":"0.48.0-38142","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.48.0-38142","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"85f68e8e2deb904af8411bd395990d5c681dc24f","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.48.0-38142.tgz","fileCount":259,"integrity":"sha512-1Yzd4DbMHN5lP2xFvMAJ4Ddw9v7PfgK2AZeygyPbOXaNi2/PAlWcXbMabuUT5BC1U2qGblQHfnR8fORXzOtgPg==","signatures":[{"sig":"MEUCIQCpw2YqAk1Lrmw67PtCSw/UsKVzljtesGJEf1ESjtBHcwIgCvov/LaFFHn4Y29n01q22FMyS67S4s5q7ICn+yrcqXE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742989},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.48.0-38142.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.48.0-38142","@fluidframework/counter":"0.48.0-38142","@fluidframework/aqueduct":"0.48.0-38142","@fluidframework/sequence":"0.48.0-38142","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.48.0-38142","@fluidframework/datastore-definitions":"0.48.0-38142"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0-0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.48.0-38142_1632240083279_0.06223260021872101","host":"s3://npm-registry-packages"}},"0.48.0":{"name":"@fluid-experimental/react","version":"0.48.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.48.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"e95e30be29635051513b473996ce6fd729bd89cc","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.48.0.tgz","fileCount":259,"integrity":"sha512-LezoyyHhcLTISG37EltYbHYTzoMzH4dB3tf97Zc5q/EI08qltzA48+CFVtoWz2W0EffAAW9Xb7ss/yCSJ+QZGA==","signatures":[{"sig":"MEUCIQCZI6bGf2mF5TSkKvEhvHxMxNvVKQBvIY4wxX3R2Bkg0AIgHnEncxbgrviqqQHYfN26p1IwYA5dIjpSpVhZKJ539LA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.48.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.48.0","@fluidframework/counter":"^0.48.0","@fluidframework/aqueduct":"^0.48.0","@fluidframework/sequence":"^0.48.0","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.48.0","@fluidframework/datastore-definitions":"^0.48.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.48.0_1632254924251_0.21113579303596985","host":"s3://npm-registry-packages"}},"0.48.1":{"name":"@fluid-experimental/react","version":"0.48.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.48.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"be0c6ef8213d68a841cf6fa9810f0fd9fce2809c","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.48.1.tgz","fileCount":259,"integrity":"sha512-SI5HuQjQEa2LwM82NfnJrXWiy6CYCrdWaoxF35l0z/cUYaWWI9tjrx27aVqWG7NLfIbrln6LnKASamhxYLMw7w==","signatures":[{"sig":"MEUCIQDWo2/MF3GjrZYUpC3bTqYTVmpvWLhBtNJUnzSs5aoR0QIgUTjvlQchMFt4hE99SLO1XjNqmuc/P/AYF3aSfeLKVRs=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.48.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.48.1","@fluidframework/counter":"^0.48.1","@fluidframework/aqueduct":"^0.48.1","@fluidframework/sequence":"^0.48.1","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.48.1","@fluidframework/datastore-definitions":"^0.48.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.48.1_1632436117792_0.13135106405414043","host":"s3://npm-registry-packages"}},"0.48.2":{"name":"@fluid-experimental/react","version":"0.48.2","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.48.2","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"e013f72b1ff4652dc3d6309232e70f4eb03e6109","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.48.2.tgz","fileCount":259,"integrity":"sha512-NfgHi7fLeNPUPGP6eB4SJuhUleMZLZy08oHI6bshfxu6u249yBgMJlin9gWQnkO2/kblokUKZ6EB648dc32NUg==","signatures":[{"sig":"MEYCIQCgqPeOZ6xba/u2c/HP9KP6sW343HAZG8TVTXKr9sBomwIhAPJCF5YFioy75aXoVuZGJtk4fG7eot/EI8/XXqPyIJg7","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.48.2.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.48.2","@fluidframework/counter":"^0.48.2","@fluidframework/aqueduct":"^0.48.2","@fluidframework/sequence":"^0.48.2","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.48.2","@fluidframework/datastore-definitions":"^0.48.2"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.48.2_1632772615222_0.9072103731247043","host":"s3://npm-registry-packages"}},"0.48.3":{"name":"@fluid-experimental/react","version":"0.48.3","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.48.3","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"c473915363a9362556f8935347b4122224aa4921","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.48.3.tgz","fileCount":259,"integrity":"sha512-CtYG59H5vHItTzJC0XUrQvxdO64w9fYR69pqBN1pc/cEniK/lOQjF3TQqy11eZyYWp/POhm7m6Qmp4qn+gnR+g==","signatures":[{"sig":"MEYCIQClfmDEmphlhOzZpR7qWNe03SxJFqbAX4wQqDCW3IIeOAIhAOAyMnYon+/2DGyqeUzbgWdQgocRuy2KrSIvJ6XtsC+6","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.48.3.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.48.3","@fluidframework/counter":"^0.48.3","@fluidframework/aqueduct":"^0.48.3","@fluidframework/sequence":"^0.48.3","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.48.3","@fluidframework/datastore-definitions":"^0.48.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.48.3_1633033829896_0.9783710993395756","host":"s3://npm-registry-packages"}},"0.49.0-39015":{"name":"@fluid-experimental/react","version":"0.49.0-39015","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.49.0-39015","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"ca22e74bbe9bd5e8e32ccf0572404f951eb34ad1","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.49.0-39015.tgz","fileCount":259,"integrity":"sha512-FfkDuOOpPv0RzJuRtd33qs31eBYS+I75x4leTPVP7otXvDd2Pz1BPtbJydqJ0miZcg0MM5r0UC0kD/V8BbaQ7Q==","signatures":[{"sig":"MEYCIQDGz3yvjPugd33/batLaoS2xHLOOPC9bTy0u//uZjNwvQIhAL767ITnJ6RmH8/6vxZJoIWdR7qTd2UzQjGiZlQTxQay","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742987},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.49.0-39015.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.49.0-39015","@fluidframework/counter":"0.49.0-39015","@fluidframework/aqueduct":"0.49.0-39015","@fluidframework/sequence":"0.49.0-39015","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.49.0-39015","@fluidframework/datastore-definitions":"0.49.0-39015"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.49.0-39015_1633039247591_0.5337428885470985","host":"s3://npm-registry-packages"}},"0.48.4":{"name":"@fluid-experimental/react","version":"0.48.4","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.48.4","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"618312045a3f95be117f702f29f517a6d4265523","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.48.4.tgz","fileCount":259,"integrity":"sha512-h1ygh+nS7K5UxIJa3eO42RCFWTmO+wQo4Fvl8YnPbP/kN1eE5gS3f4V5mf6wbEHQiDQfcvsDWlWRWyivpRoIlA==","signatures":[{"sig":"MEYCIQCYs+LE57P+73Qfw3Np5q25hod1h9mbIQ1XBAJ/Q4q46gIhAN+t8IK/9Vxu3OFMxoVjoUq6bv47LoDLmFbfaRpZ1KO/","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.48.4.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.48.4","@fluidframework/counter":"^0.48.4","@fluidframework/aqueduct":"^0.48.4","@fluidframework/sequence":"^0.48.4","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.48.4","@fluidframework/datastore-definitions":"^0.48.4"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.48.4_1633108863307_0.6234166365238154","host":"s3://npm-registry-packages"}},"0.49.0-39313":{"name":"@fluid-experimental/react","version":"0.49.0-39313","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.49.0-39313","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"7194c5625a47674045ee254bd2cb4ffb7cfa013a","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.49.0-39313.tgz","fileCount":259,"integrity":"sha512-YcnJYsfJ7sFE/noI11zWTyjaU98pFocUlpwjYnMI2GgJA3REHM/QwhatEBaa2EZMJz0HogZ0ROevRWYVE0mHxA==","signatures":[{"sig":"MEQCID2qk+BH869KBWJw6txNpQl6gOTqxwlR+FYRyGpA8bf/AiBD7/6334a5lhvrJUNngWtRQ0LPfV75yJWt1PBaBOsvgA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742987},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.49.0-39313.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.49.0-39313","@fluidframework/counter":"0.49.0-39313","@fluidframework/aqueduct":"0.49.0-39313","@fluidframework/sequence":"0.49.0-39313","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.49.0-39313","@fluidframework/datastore-definitions":"0.49.0-39313"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.49.0-39313_1633389743211_0.04193853663356628","host":"s3://npm-registry-packages"}},"0.48.5":{"name":"@fluid-experimental/react","version":"0.48.5","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.48.5","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"91474c93f197d43ff0483380c5d467a0a20d8cff","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.48.5.tgz","fileCount":259,"integrity":"sha512-GAMN9stJZGUAn3kCl3Csip3s8hSnMKNFV1aolZemvlt7bH8B+m8hEcq3li7AYY6p6EK3gLmF1j3tXz7V7a662g==","signatures":[{"sig":"MEQCIFCCKQNIEmMe+0IjkVeAgEITuCCxi7rxFL0pUHyb6ohsAiBfNK8y7Tx2gf2j2AAgZtSaH/xYrwuBu8WJBAE3nwQi8A==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.48.5.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.48.5","@fluidframework/counter":"^0.48.5","@fluidframework/aqueduct":"^0.48.5","@fluidframework/sequence":"^0.48.5","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.48.5","@fluidframework/datastore-definitions":"^0.48.5"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.48.5_1633471121863_0.31048844008981513","host":"s3://npm-registry-packages"}},"0.49.0":{"name":"@fluid-experimental/react","version":"0.49.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.49.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"c51185e216900244e61646f042c9845532ab80b2","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.49.0.tgz","fileCount":259,"integrity":"sha512-MXEkCNtJaeVnAQB/jz9EKHuucj3Ywc1YrjPd57EpNHYAEqMIJIk3DHLR+HaZ7mVgY0a1I/0YoT6ec/7+xxT9Pw==","signatures":[{"sig":"MEUCIHnIAvNuV77ugWAJfnOtUTkBoKtZqQoAjuKy64jmEAXmAiEA1fMjjvAW6wxRpioB8yzQjm2xzvKRE4iHX3ldedeI+5c=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.49.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.49.0","@fluidframework/counter":"^0.49.0","@fluidframework/aqueduct":"^0.49.0","@fluidframework/sequence":"^0.49.0","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.49.0","@fluidframework/datastore-definitions":"^0.49.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.49.0_1633561290641_0.6631668169202503","host":"s3://npm-registry-packages"}},"0.49.1":{"name":"@fluid-experimental/react","version":"0.49.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.49.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"426e3538efd9766a7f18281756a1107f030e677f","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.49.1.tgz","fileCount":259,"integrity":"sha512-2z1hhm8oYJh8u6unNOHOoRjRZ2BqGPaCkqna+9G+pCILV8TCQF0z+9zG240XAC7aGtEJm2sNNmia7vhf4pFAug==","signatures":[{"sig":"MEQCICsATKqeUx54aRhHxTO79KX7eaih9eL0UjjHB/v0fqzKAiBt4NVJ8OMUk5yChfN0nUjocR7FFxfSULG2vIjqqMM+fQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.49.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.49.1","@fluidframework/counter":"^0.49.1","@fluidframework/aqueduct":"^0.49.1","@fluidframework/sequence":"^0.49.1","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.49.1","@fluidframework/datastore-definitions":"^0.49.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.49.1_1633969413857_0.14280472139925782","host":"s3://npm-registry-packages"}},"0.49.2":{"name":"@fluid-experimental/react","version":"0.49.2","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.49.2","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"da2dd546bcc203ae02ef6f3f6f7db9feabc4deac","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.49.2.tgz","fileCount":259,"integrity":"sha512-uFdHdnhY3A1b+wy66ehS0GaI3trtduW2KYpn1+WlHjLLJ4U5QCIhinraGoJRwcWXwqDiS4iFf5f++G6h31s4Bw==","signatures":[{"sig":"MEUCIQDSn0v1xvnKj7UQlK2ZqRTZzO82C3GDfL4OePnPS5/yNQIgWPz6KoZaXbnK6UUv4QLvFL9jIhQxI1+78+ly+KX8t4M=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.49.2.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.6","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.49.2","@fluidframework/counter":"^0.49.2","@fluidframework/aqueduct":"^0.49.2","@fluidframework/sequence":"^0.49.2","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.49.2","@fluidframework/datastore-definitions":"^0.49.2"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.49.2_1633991002860_0.08165639108382017","host":"s3://npm-registry-packages"}},"0.50.0-41365":{"name":"@fluid-experimental/react","version":"0.50.0-41365","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.50.0-41365","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"70fae0523670383130d8b85187894f4281f697da","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.50.0-41365.tgz","fileCount":259,"integrity":"sha512-/ZqrBsfaMBgxhSHsP2VG+3biOMARHgDZse4p/wLwiS1gzjGuItOwTQil4h6gaKd7cMqlLK8v6q+I7UmB0YYIYg==","signatures":[{"sig":"MEUCIQC2EWbcikZv2t2rPKDBmNeZwNT8Bd+dmSzrYbs1qzKzNAIgVbXx5DPhmeM4ZZrsjMPbZ2QPsJMwXdpj9T3cqkgEgrA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742987},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.50.0-41365.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.50.0-41365","@fluidframework/counter":"0.50.0-41365","@fluidframework/aqueduct":"0.50.0-41365","@fluidframework/sequence":"0.50.0-41365","@fluidframework/core-interfaces":"^0.39.7","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.50.0-41365","@fluidframework/datastore-definitions":"0.50.0-41365"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.50.0-41365_1634835730838_0.8117805852892739","host":"s3://npm-registry-packages"}},"0.50.0-41540":{"name":"@fluid-experimental/react","version":"0.50.0-41540","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.50.0-41540","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"8bc84ab0311551873825de802505caea98845c9e","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.50.0-41540.tgz","fileCount":259,"integrity":"sha512-pu04S8CSBZzxwTbN9ETYl/bIlvDHHTdiRbX0xI1UVCjJr12sdK3dfjcO0H/S5E2gU+FY8DuEfCknxpSPMUiVbg==","signatures":[{"sig":"MEUCIQDnjX3NEWy50qMsPhZ3+Ti5EdWN5nyBV1X9AxfD0FpUBAIgfpVHwqjEJ2f/3MUMIsWS/cQt80GM2I/EFV1cEP5A1Co=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742989},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.50.0-41540.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.50.0-41540","@fluidframework/counter":"0.50.0-41540","@fluidframework/aqueduct":"0.50.0-41540","@fluidframework/sequence":"0.50.0-41540","@fluidframework/core-interfaces":"^0.40.0-0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.50.0-41540","@fluidframework/datastore-definitions":"0.50.0-41540"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.50.0-41540_1634917873495_0.7810687561960965","host":"s3://npm-registry-packages"}},"0.50.0":{"name":"@fluid-experimental/react","version":"0.50.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.50.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"a2a0f5587067d88d307a5661d997b237cae6b4db","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.50.0.tgz","fileCount":259,"integrity":"sha512-cstmPSMFu/hJH4x9RPqilfFnbA7081JR/LgtWpTSHHfBFLpARGSAOtAhQX9IV8D4pcohv8CoAYc0j5k731WLTA==","signatures":[{"sig":"MEUCIAaz7ONLVUFWEzCzoNpfcxRzoiGUH5meAQtIjXt0FV7YAiEA8jdvpWEoM2VVSoQX6zgmkMngKkAVtD3v+tRyZNGCid8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.50.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.50.0","@fluidframework/counter":"^0.50.0","@fluidframework/aqueduct":"^0.50.0","@fluidframework/sequence":"^0.50.0","@fluidframework/core-interfaces":"^0.40.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.50.0","@fluidframework/datastore-definitions":"^0.50.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.50.0_1635180599138_0.9935284040605672","host":"s3://npm-registry-packages"}},"0.50.1":{"name":"@fluid-experimental/react","version":"0.50.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.50.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"d46e84c1613463413757ce1d60edccb1e3f02736","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.50.1.tgz","fileCount":259,"integrity":"sha512-UbcZbLcjWeXVSy0uvEER93uU5lX8ZHXPSvPnj2JZ9LgY8DuzPQcqiKaj6mrgrdVIQ/WMza3mLnq+Q+ZHDkyFjg==","signatures":[{"sig":"MEUCICDmAegugQu1RhBdm5feYEKk1M0M6xsk46o9A4ZSErRdAiEAs8YWDcmNOpShYf8A2QzKdUHTrjsyWWzBQXtFwd0ISK4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.50.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.50.1","@fluidframework/counter":"^0.50.1","@fluidframework/aqueduct":"^0.50.1","@fluidframework/sequence":"^0.50.1","@fluidframework/core-interfaces":"^0.40.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.50.1","@fluidframework/datastore-definitions":"^0.50.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.50.1_1635375307264_0.005942517265155045","host":"s3://npm-registry-packages"}},"0.51.0-43124":{"name":"@fluid-experimental/react","version":"0.51.0-43124","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.51.0-43124","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"76e0cc6e31c5e29430b79913bb6d36f0c6ea9103","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.51.0-43124.tgz","fileCount":259,"integrity":"sha512-PMwCaUw3VvcXV366XO7Ua6PR14LJDS6pBimsVUlNjeycz+mbdImCR3U4BP+ITLAfiG54gZPykv2J2w+g5ou/Hg==","signatures":[{"sig":"MEUCIQDJQDWR0pnvaJ3dV54fHTEBKMLvgn37F3BrWLUsdCSs9QIgcFvCD+luzZ9Yq2iEHnmQ5622CBQ2pDk2WJFPKo7r5sw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742987},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.51.0-43124.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.51.0-43124","@fluidframework/counter":"0.51.0-43124","@fluidframework/aqueduct":"0.51.0-43124","@fluidframework/sequence":"0.51.0-43124","@fluidframework/core-interfaces":"^0.40.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.51.0-43124","@fluidframework/datastore-definitions":"0.51.0-43124"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.51.0-43124_1636158392506_0.5971312265938757","host":"s3://npm-registry-packages"}},"0.51.0":{"name":"@fluid-experimental/react","version":"0.51.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.51.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"224dd73dc00abd23dd099d5150a25a48499cf0a3","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.51.0.tgz","fileCount":259,"integrity":"sha512-g5Pzoec7gsmSUA9jAGqNazb+Cu8mibFbMqmv0opSJhqkxBGr82iVErmToEwWv/C0av/VznDJgxvmbNpsBgumIQ==","signatures":[{"sig":"MEYCIQD2MA0Dt0tWqC/ZLaE9yzSOjjGMoaNCWYvgjJ2DaCVPAAIhAKeF1dgGF+DEcvgpn1fwYzesXESmhuZTzmMGTGTXu57H","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.51.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.51.0","@fluidframework/counter":"^0.51.0","@fluidframework/aqueduct":"^0.51.0","@fluidframework/sequence":"^0.51.0","@fluidframework/core-interfaces":"^0.40.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.51.0","@fluidframework/datastore-definitions":"^0.51.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.51.0_1636582831402_0.9673018582624764","host":"s3://npm-registry-packages"}},"0.50.2":{"name":"@fluid-experimental/react","version":"0.50.2","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.50.2","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"2c7861408732bf412faf82bb28d4bd81df9a407d","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.50.2.tgz","fileCount":259,"integrity":"sha512-hVZd+o+NrXnymlHM2/YYgx1gDey6iCmdK/R51EXUS+rTLbQPij7rzpUQtEd8xUQfT26qGvj1awSJzGuFUyHCjw==","signatures":[{"sig":"MEQCICKZSE4WeMoqyYU0I8fy2h2/CHKlDJKjuaFQG5OnXQwcAiBlFE3s5KjPndBASLNGcw630FQumXQ3c3WWNaFm9Cb45w==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.50.2.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.50.2","@fluidframework/counter":"^0.50.2","@fluidframework/aqueduct":"^0.50.2","@fluidframework/sequence":"^0.50.2","@fluidframework/core-interfaces":"^0.40.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.50.2","@fluidframework/datastore-definitions":"^0.50.2"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.50.2_1636751593458_0.08431279920562318","host":"s3://npm-registry-packages"}},"0.51.1":{"name":"@fluid-experimental/react","version":"0.51.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.51.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"107742f76389fdf717de856b330830738a0d5711","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.51.1.tgz","fileCount":259,"integrity":"sha512-DxgG+boNyqBba48Lrh4Uv1mrVfoXpdTRV8Rr5wUnKIunXfK8bRtYQQzQoKwPadIgEui75pq5gK77z62H2Dpjlw==","signatures":[{"sig":"MEUCIHo0tzHKY6Aqrdg27FkpWuZWfKiM/ipR2YoOyIe18+mVAiEAkXdyEeRR6D9PKTGVOs0uzRf5jw7zgHfcLlm4LGL5hiI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.51.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.51.1","@fluidframework/counter":"^0.51.1","@fluidframework/aqueduct":"^0.51.1","@fluidframework/sequence":"^0.51.1","@fluidframework/core-interfaces":"^0.40.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.51.1","@fluidframework/datastore-definitions":"^0.51.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.51.1_1636754492744_0.997540469765722","host":"s3://npm-registry-packages"}},"0.50.3":{"name":"@fluid-experimental/react","version":"0.50.3","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.50.3","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"5872bb366923d5789c8f33e53a2612c9008744ac","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.50.3.tgz","fileCount":259,"integrity":"sha512-JwOqRgFAb6ydhzJtmwdX7C/uUcPpl/AfjLq93xuQcow4l6AMbWn50a2qrA6BfbLiRhxlVkuhKJSlSOvlYKKeiw==","signatures":[{"sig":"MEQCIEzwNqVUVg1STG2n5q94Mk+8cDAWixGVvpP6+YsCV1atAiBOwpykGbR1sspRghrMx30tpcwvkWTt12moJeHoI27CUg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhlsvkCRA9TVsSAnZWagAAQTQP/2aNRFdq9AwinUBU2FCZ\nbVNBXzMTvPwXMNqqZ1lUiG3EeMfVD60wxd9sAxqarQdoISQnKE3agOBQ4cxH\ndLupYjZqEf13m6XI7sEOE3l9PXlmuzrsP9KQMcEZIRYgbZW4aSoyOFgnAf1y\n2IHuo8+jfzlzgr/iIcgKNRxS9G+Gg92xdUrSLWb3ja/4aGTB49KpW2tCXV49\n+I84sOT0OpADKOiUx2ZLC+HuLxf50D+Gi2A6jHuUSEoQNWENi/q4erJBZFor\n0DHs8eNy4MP/GqtatwWtAdWhWK+xaWesGYqDBPwdgj+lLfWQEM9oGf9H/7T4\nHaCPCuD/FLhyTWr4+Ihl1oulRiFOL2+oCbFpjEnil7+Fn0ahTue4VFdu7ZDI\nWOY4OAeOSI5pAUh10fxh4kBr/JVgZGgKhxno2VOFpKdkDS/l0l4fyh2XXEdw\nNK6lgLakT2Kya3VOuDRomlihqEIHVZBGIhMLWNVQquyYI+kWKpUZKkRTFKep\nIglFf8iQqoVpBhyIPTWO4f+NtcGtfr7b9onnJz18slnnWNJr+D6V9RyEm71T\n6NI55d8H1RiMW4+E92ixRGXD1zTBW7Cnafddpj/nhh+JstVh0IA8HEDFUT9j\naixzUhheCUMPT4vhyDOlX8OD2Yv0U0gTvVHW25MCmA2Dfa+tUdQPP4kINX6j\n4JJU\r\n=HAGF\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.50.3.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.50.3","@fluidframework/counter":"^0.50.3","@fluidframework/aqueduct":"^0.50.3","@fluidframework/sequence":"^0.50.3","@fluidframework/core-interfaces":"^0.40.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.50.3","@fluidframework/datastore-definitions":"^0.50.3"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.50.3_1637272548301_0.3386059256534757","host":"s3://npm-registry-packages"}},"0.51.2":{"name":"@fluid-experimental/react","version":"0.51.2","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.51.2","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"1ff21194e0ee2167676e8601becbbf373f115308","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.51.2.tgz","fileCount":259,"integrity":"sha512-GP79fohg5zGe3EvCHp717YFbxmw4m9opIC6Dvy/cs2Ao8pZzp/GKCQJyfBb6LNSRq//ejhF7006UsxkfCeEoXw==","signatures":[{"sig":"MEYCIQDQo83LKt5Bv6wh9/fFub5RB8PhZuqI/hiJ9mWoG2yb4wIhALVe0INlFaHgShCXNCfoc2EdFu+Qu45akGpCKkkUbB4B","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhltfcCRA9TVsSAnZWagAAe9cQAIJefqDjJ/pqUksclqP0\ndWwW/3fbIwwUOFpWSU5hahb+k6goSHw2zoCQJEWrfAVysNImyVyYz1/CvCbZ\nYzJGbJqb6chCQOjNZgyDvj4JaSrXlEIUe+i4jaewOegUpyLaGBvXjFWd427v\nta3wmhMtMi1nJgpPqjftDvx0d1ibb8r4hgrOnycWiWf1I5d0+4igawcYnbn2\nNH9JAoDg8nrs0ylJiDyve46ag6d1vI3SsZowJPHPMLI+3xbRYINZTDhrv7bn\n7Nm6bOFq1H9A99pVGRQcIWNh5hPl+Fg3V6gLHvzPz1FT4YeSk3jSjUE9xGho\ncuVmjwt7rtYhZmeSCwHfzjNFPqLkBA6+qRmkqz9FHtHx06SZpOr2Y3zNToW6\nqiurZhB79JyV9GrqZzuhy847mhoNYatp/LtTf5mJa42S1AQa4P86Jw54bpAA\nFuNn8+sLnW++1xlSi8r8RBp+DzT/3WPMjfCxBOxCt0/9aH5L8nJKDAZMzEyI\nUWw3NlSiYCEm+wFsqZXcwAT/ZWfhW21AtES/Epypj8dXpxt1x1YjvS/N2VjG\nfQqmOW2Zo2Vndnqzs8lUdKlwY+U+BCUtBV5l9Z8S1uvg/fMWscXHdQoI5TfT\neBF8ACIdoiTeROn2hE9P9wu3J4teyhlTmuteMwTh/V48GRGDsGSDlL42HVi0\ncgOt\r\n=MLYT\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.51.2.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.51.2","@fluidframework/counter":"^0.51.2","@fluidframework/aqueduct":"^0.51.2","@fluidframework/sequence":"^0.51.2","@fluidframework/core-interfaces":"^0.40.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.51.2","@fluidframework/datastore-definitions":"^0.51.2"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.51.2_1637275612078_0.5407980336837916","host":"s3://npm-registry-packages"}},"0.52.0-44610":{"name":"@fluid-experimental/react","version":"0.52.0-44610","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.52.0-44610","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"445f50984f99c743c5b59d2073a0553e8d7abdcb","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.52.0-44610.tgz","fileCount":259,"integrity":"sha512-ewSwk1/TPo3OpHfzYue1AwMqvLEHat8BjksIloxRp8Hrqi26vi/IFqltMh6bTGv/Mag9brD0AqSPMLnVH91YVg==","signatures":[{"sig":"MEUCIQC7wT2hNdDoE4YDTpPoXB8qcKykdAVCgDPU8mdTuLSaMwIgAJ8Xzh8Qa9FaL7+eLNZVmuvLS1Fz9rfNMNFe8fpIb5w=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742918,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhmCx1CRA9TVsSAnZWagAAkccP/2Bx4CyOBR8NjIWiFqSi\npYJP5AuVz+r7yjGAahSpavKepspexjjzuA9bflgjfpr6+SWSyPW042FzE5Oi\nPjcFh0j/wPg7v+HHxSnGGQjt0VHzO/Gv3m4A4bTLpqbPviRVN/JZ8+ZriMHU\nhheN5MfftqAl7ks3CwAOmKcA09Tver3gsV0ZEtkYOYZJGnXHxTCbrW1o6EX1\niC82GGMNseh4IxQD12+EMWFyIKQU14YJOmbhxY3QqD2CxNKzexukk5vm99ht\nlE24Woeaa8NnKcEDOMN0WHQXfGeRxoBCJOpj1iq0liURkLvsp4jMYt9PiP/b\nNu6tg0JKw39rL9Z19uY3r+FSoal6lP2waJ7mP3PxYp+eF7QXe1+qYjJkvVbm\nUyXnuA/A8ivFT/hk+HCNB/dRxjjNnn2kIu4gUVQj4NMM6CNyewnhrcBTnaqf\nNwhSF6F+umCJD4MGq5XLGY+cFI8azmZrH3EZnHhEzexKwHcKNEy82bBfF/16\nGi/Kc8I764gmhnInwGDGf/cXZcATi0F9xoi/a586vjpcqcqho6c0VTtU5UnP\n8fQQYHss09ke4InSAeXx4sHjTrYd3YvUttKbGaX9cIEE6MM960hqpHJDz11v\nbHU7Hja0U8bafa4EhvTej0KrOinQaSdzolknIjlkFGkTzvIhtGRmSPiYr9Ru\nCk2H\r\n=zldU\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.52.0-44610.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.52.0-44610","@fluidframework/counter":"0.52.0-44610","@fluidframework/aqueduct":"0.52.0-44610","@fluidframework/sequence":"0.52.0-44610","@fluidframework/core-interfaces":"^0.41.0-0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.52.0-44610","@fluidframework/datastore-definitions":"0.52.0-44610"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.52.0-44610_1637362804854_0.6041734945381074","host":"s3://npm-registry-packages"}},"0.52.0":{"name":"@fluid-experimental/react","version":"0.52.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.52.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"49673a6db92fa1f16662d29bc9df9358dee382fa","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.52.0.tgz","fileCount":259,"integrity":"sha512-RPX6fH+Am05YE5lVS9SiSB3aW9094APiC41/GXi+yUuOzp7XyC+3wj+biE0zaA+vxtfN8EaIKgzCAFE5wZaoow==","signatures":[{"sig":"MEYCIQClXh2U3GR/gfoVKKC25jw2/NMho4Aqs3TqtZgO5NDNVAIhAJFaEMbX/5rzPQWeDEPBeC2Eywt5kP3dAQueHx3y7iZY","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742880,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhnBduCRA9TVsSAnZWagAA7NIP/ia0W7nRXFQi3D8M9aNX\nXf96F3gq5qJoCpMtBa8V7YxkROGFGRvxMhyHX8bU81ca8OOwp4juJkN9y7Nt\na5uzWcxUSIqGle9WK5I4c+4slfJ2dvysy6D2lkMmaP2UV3pftjlbviovMdCV\nYtfchu1KJjXxOS+McSnKKtLTokrPthicbFLVUnIixL/s0r0fG4j580IPXYR9\nFUpFpvSS67GbrxFUqFpkqImigzPQBQee81Bj/DFZIG87LQip/0ntN5lFjZCb\nt8kVzSM67fXbaaHD+H8eLb5m8nHVcbVM/gZgDguPX6h7GVT/yypG0i/Gmsue\nLJrSUEQKZBoFelNTANPZQNKrMpQ1KfssqRKPxDFRTPPsPPkFZXKeiRpaZgII\nlkoz+DVxNp8CWvy2ph5M0zfRUO52jc8ijsO18/toXN8NJvl+KpUUEu0y8scZ\nIcztkGji5Qw/BJESzu4drVZOYYR1lgk7D3A6tMSe7TbkGmF3ohbpiwPH2+kW\nK2Ah/PRp6wRaRJy0h+I6I3DJR/QmsPDSSaqnkkaXPvfeM10TuEqbujDaVkyo\nIMeXF80qucEM3ly/QLutzg6d4C0Ep6XB9cXXjDpuOno942LTGr6XGOzZnzy4\nRY/nWhHJSoXHADOyikARlUR4rsDfslGV3TlkhKHPPLVpmbI4udtSqC1nGNxS\n4LZP\r\n=2xLo\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.52.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.52.0","@fluidframework/counter":"^0.52.0","@fluidframework/aqueduct":"^0.52.0","@fluidframework/sequence":"^0.52.0","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.52.0","@fluidframework/datastore-definitions":"^0.52.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.52.0_1637619566114_0.8511590131049971","host":"s3://npm-registry-packages"}},"0.51.3":{"name":"@fluid-experimental/react","version":"0.51.3","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.51.3","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"bcd72df0b0ba682a38df0ea8f465350e2a25c6e7","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.51.3.tgz","fileCount":259,"integrity":"sha512-uvsrFPUayXng1LKOMe4OoUSEGiyCE6r6ca6pdozn2enPw6ed1XRhY1x03p1kPjE7F1mzWNtUudpV+UoJgmJwMw==","signatures":[{"sig":"MEUCIGwH8eIlAOmQbYoiC4+b5EfSorND/N4d3rrNsOHGc4qHAiEA0yWKxVz7Jnwye7E9bP9WEhFJyQVhuGbL5Nb0erfLvdI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhnU7cCRA9TVsSAnZWagAAF+0P/AxQRqA7oP5CW14AZdNo\n/EseJ/c9TiH6rmzK3Tk6tJxiqzTpQZqayPTT8RXvehnQ87rCk4xASm5nNW4L\nBTwTNVUi959ovYupcWccdrQ4C+WoO8LpGXtp3sr7V5+8AeWZwO0LKg8k1+Hb\neis6tk/24enfNaYu51BZJs8XJpbKroEuCbZdJToFe4639uGwCI3jjuqIYq4P\n2w6CNmXFenugmmehXm/UaUaL+Zirw9ib5wQJae7qCzVAgdRnathOh3Vxb+Fk\nLegxjXOp84+DJdUlqoa2KaRQQfmsKpKcicT1lRr0xR2jhThWq/QCAX6CLGFc\n4sgBTtPxviDINhKdQTpAYlDWZr76QifkNpK85SYPERB8T2RoKafLBpNyd454\nMMVl3mx2ZwN6S1jRm4E4j8ytyDd864bos6EAvZjNqvOlas8xQcknyhdxuIc0\n1X5ItzjdbhaAiRSWHWrSbTyUfX1A5+JUY9iSeZxw1HSM69flY7B0fCukW8aQ\n0Qwrpipz3xfF9Q3bl6TiGTDMETVe3gb7tiJScRSiilutv1YJLJL+oRniES5q\nwIJwZloTKwF7ZTsY3nHHd9in0jJ2mILapiK20CJ3n7MAZCDbmA4dN6FXZ3XU\n44CiO5FMDDpE/Z9sMHabb6lMCt/JRKXp3d69iagWGQs9px1aLAZSVi6xlw0l\nRAp3\r\n=0rG/\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.51.3.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.51.3","@fluidframework/counter":"^0.51.3","@fluidframework/aqueduct":"^0.51.3","@fluidframework/sequence":"^0.51.3","@fluidframework/core-interfaces":"^0.40.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.51.3","@fluidframework/datastore-definitions":"^0.51.3"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.51.3_1637699292444_0.402406692524216","host":"s3://npm-registry-packages"}},"0.52.1":{"name":"@fluid-experimental/react","version":"0.52.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.52.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"d4353635ebb55fdc3ae411b69959c559f3338389","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.52.1.tgz","fileCount":259,"integrity":"sha512-Bg1z+0N4YRkcTJMlL8hDToH2JkrzQqiNtwD+RxyD6Z6ZsnIBAiFhAO0tmx82tTsAC1NEnVG6nSL3OJDS/DktkA==","signatures":[{"sig":"MEYCIQCTs5ZxDU55duO1O9TfggEeWOvnySwMNHHhxf9CxysKsAIhAJ+BowQAmdSb2oHhOeBaXUAY29+6wVumRHoOq9BwfcPX","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742880,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhnWFsCRA9TVsSAnZWagAAtoAP+wcKTHNaFOr4C5IVhtiB\n/4SR1c46xrLFhox44H0klUZw8VL01wpfru2I6UzExQnqf0cEQElFYgt3GE1Z\noyvMuMb2mpoAGnPoZbg5XpFX9GeBz881u0tjT0dK2FPwR0MarS7n7SJhiKoK\nDXDwkw1A/WQ+qAHJjg1MxuyHnKMjooQU1hJ6O68owp2nms5gnquRMZJTMYwf\npevdUI0Jz/E0AHhGoSrH5UdIc6ueO/LCTyMnr6itmmmP036Ko7Asoa4rcwM6\n3SYpK0ofd0nnxVDezYF3khtz2macEFx4aLRmUtpZsrOVKk9MNaZeRYyCn9qC\nN/BNvFzCHtoRXW5ImqpnfgJpzyMNcmBxVTx++QR4bORQt9k3jQ3RRpvL6h3Z\n+RBqVs8pktrhWl25i99kQSaRrbU9utY3FQZGeJQ6BbY88Oqd0ZWcdw4aJyHp\nNS+w4R1qgoE/GKb7RSQsfvrgOQ33GmkOqjixRIV4DDnfaS/BnU0RZ+uslOdA\nv0d18cMb7ytoveTmaFfpj/BoZIqYzDx2VE+LqL0R4wQzWueHl7JW34BYiYeq\n2y06hmV4ouydtKG9jl2cJ4+3k5seRLenBWsUwJaris32yP2gmfKfVeN+fP9p\n0NoJLQT2QY6jl1B1PsQZ33Ai6k4PxPDzrN5I1HVpRedt1A60D60UhpV1Ckpg\nFwqE\r\n=1Lay\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.52.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.52.1","@fluidframework/counter":"^0.52.1","@fluidframework/aqueduct":"^0.52.1","@fluidframework/sequence":"^0.52.1","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.52.1","@fluidframework/datastore-definitions":"^0.52.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.52.1_1637704044432_0.49996506232564286","host":"s3://npm-registry-packages"}},"0.50.4":{"name":"@fluid-experimental/react","version":"0.50.4","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.50.4","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"e6ad868cd4ce23627a07d52d649a22b04f8984bd","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.50.4.tgz","fileCount":259,"integrity":"sha512-TYNX+ohdb9QJ1jRi37CNzKCVJ7vsfGh65a/y/T+hy2GJ5kwz96W6sCnfnyLocSEhFTKPxM9cFa1YU6rep29sdw==","signatures":[{"sig":"MEQCIAUqljgIkjlPUN/jfk2fa7P6X2SUO2AkU04ZxyGTX+dWAiA4z+dh/BIYu/ygY2VM7a1HD91TeELC5yNY4ND3nUkNdg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":742951,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhnYO6CRA9TVsSAnZWagAAIKwP/jC/E+u/nBwGMvcd75gU\nucpq0p1Ufgtf3J36pT9whJY92+LbrT2gMPhZA2V1lhDtQ8/EZBy+rFOiKfUS\nND/xvdWa4zgVnDITtpwMgxSY558ELHIKk+jyXxsO2MrhiV7h0qo/afMOd5ZE\n8GhEpjzC+xYPy7yiQx+xE4UV9uVmFHgm9IbnonBX3QfdbRzWNnUnodzGVy0i\nU2Np6i+V5pryo2KuhsFLQ+SnzaflLjagxmAdJuAgJuErB+HAzj1kdXIuWH22\nCWT6NiUAKu8o+fukGaXv8o0UfkhqTqxA0LjkKBjXK7kk6MXZe0r0hr8cPyN+\nmqhLB2ahAlk81Y6CgsyKO9J8Xs3Mrot/C9k36NHYRA8XGdjTM/1gaQztbdFr\n2II7uPXVVHHIVsQ1AYHAz7UPeav/IfhWBHQu2fIWCp+IkKwKysrnPcHhWgK3\nu95cCy6S9jH++kjqOeGqMQ155fmsRRifQbC+Ovv6jQ4EXjqWDqTrWjSaeJ63\npMdLxyYy6ATzwT9Cw4pYHKPKzMfgBAT1iO5GChgIYhSGqHLvPBvD/PPiesDh\nTc1BHrF6lpF9G+3KwnvJ7Px/ku0tPgl1SlCVVA02ue7tkdAaeHlFoaMpO/FZ\nkg6Sbh5M3gJrG8rNWb5vJtwjQbUnadPEMCnWFcXky2ehdhsWIMP97y2LSL9U\n1oAV\r\n=JBbn\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.50.4.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.50.4","@fluidframework/counter":"^0.50.4","@fluidframework/aqueduct":"^0.50.4","@fluidframework/sequence":"^0.50.4","@fluidframework/core-interfaces":"^0.40.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.50.4","@fluidframework/datastore-definitions":"^0.50.4"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^5.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.23.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.50.4_1637712825878_0.7055129704535812","host":"s3://npm-registry-packages"}},"0.53.0-46105":{"name":"@fluid-experimental/react","version":"0.53.0-46105","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.53.0-46105","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"5d6778850879fce4014e17fee9ee627cc1244e54","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.53.0-46105.tgz","fileCount":259,"integrity":"sha512-ozt3ahwq5UrYptzo3WHGyP/5ecPVIgU9Zlcs3nBBpGN7C88qwuq6jYHgWOUninwEQp7QQ3lMs69TSkyNmjyppg==","signatures":[{"sig":"MEQCICKW58kKABgowydSP8VjmNT3aXOtpbB3ZJRP7q2ngDHjAiBNTss/Gp9XHTGC+ixiASfj2K08kcNERNb3iTkLTDSQTQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":741913,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhsTqCCRA9TVsSAnZWagAAsXwQAJfBrwlH9aPurmCLOyO4\nE/1FJVYgFOaEYO0kjZAVVi2sd5SrLOpkukaCx3nDD8gn8WorI/k15ejaA/aJ\nuWwaUqBp5DA3KJ+RHnT0FpyXfwM0Rc9OrzUBTNSjNQA5In8MMFo+alLAE2+S\nLbUiphnfIcUuC2sPCtOGs2Cp9wi3qnKk0aZ7HradNjtQ4idMG6Wv2WmXFvt2\n9al5U8jL6G8dMXYQ1rzmXsMq/uoxNO4qtiA3E/TljvisxeNs4qIuUbT9BPOV\nYk8vT1DBFqKn//6WAY6d/AH9mgaxvA3CjIzMAvTMkBXRD7YnIRG58L5c/Q1/\n8/fKvwkSL8CofyBFj6m6CzvBRXAw0YlCdI3+W3rwbNlLC+XZFRauzSHR7pJo\nznsrjPY2ezTMdxWdJizj3gIw1fqDL03kAl503FUx8xjf2c8o2MNs+/mjC3z5\ngJVcFC46pqyeXogNmHAdg2/1sXDXSj/u3luggcaIBz4HgBqAh4mfmRdHt9VQ\n+U7um2wOiS+nwOazhY3Fo0hHfSBAGUccm/Yann+03W8Vun8ErRVdWo9eqXdk\nVeKJQi0dkgx86cKer+QSbVhjRAe4d8cvrpnDArAEwlAZvWEeYdUVpw7nkqDM\n/uIwFM7SLf9UN8toWk5sPVQcMPhMbNWN4TbkvA6k+aBhQRtMMBa7fnEnxUEa\nHqXW\r\n=4w4i\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.53.0-46105.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes-and-comments`](../../../examples/data-objects/likes-and-comments) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, `setSyncedArrayConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class LikesAndComments extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced array to config under ID 'comments'\n        setSyncedArrayConfig<IComment>(\n            this,\n            \"comments\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedObjectSequence` -> `useSyncedArray<T>` & `setSyncedArrayConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, SharedObjectSequence, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesAndCommentsView(\n    props: ILikesAndCommentsViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [comments, commentReducer] = useSyncedArray<IComment>(props.syncedDataObject, \"comments\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // Use local state for the comment as we don't want it to be synced until it is submitted\n    const [currentComment, setCurrentComment] = React.useState(\"\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Convert data to JSX for comments state\n    const commentListItems = comments.map((item, key) => (\n        <li key={key}>\n            {`${item.author}: ${item.message}`}\n        </li>\n    ));\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n            <div>\n                <div>\n                    <input\n                        type=\"text\"\n                        value={currentComment}\n                        onChange={(e) => setCurrentComment(e.target.value)}\n                        placeholder=\"Add Comment\"\n                    />\n                    <button\n                        onClick={() => {\n                            commentReducer.add({\n                                message: currentComment,\n                                author: getAuthorName(props.syncedDataObject),\n                            });\n                            setCurrentComment(\"\");\n                        }}\n                    >{\"Submit\"}</button>\n                </div>\n                <ul>{commentListItems}</ul>\n            </div>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesAndCommentsInstantiationFactory = new DataObjectFactory(\n    \"likes-and-comments\",\n    LikesAndComments,\n    [\n        SharedCounter.getFactory(),\n        SharedObjectSequence.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesAndCommentsInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` and `useSyncedArray` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.53.0-46105","@fluidframework/counter":"0.53.0-46105","@fluidframework/aqueduct":"0.53.0-46105","@fluidframework/sequence":"0.53.0-46105","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.53.0-46105","@fluidframework/datastore-definitions":"0.53.0-46105"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.53.0-46105_1639004802700_0.767206241297125","host":"s3://npm-registry-packages"}},"0.53.0":{"name":"@fluid-experimental/react","version":"0.53.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.53.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"2a47fbdab54a2967df6c94c08bb2a1196cead1e5","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.53.0.tgz","fileCount":259,"integrity":"sha512-xQbnAyjpwlfBvQ5J2AjLT0sm6nrMR7t2UTkYsJkds/DpNFZXn5qsQ4gkjN94RkND4/iB1+yNzWo1DLGbpcVY5g==","signatures":[{"sig":"MEYCIQCfU2RjSRY+Ki+z05XLnZUT9MUMSYC1E6005RWAJ/9SzAIhALd5Sxc8rNDrzSQAkg3LOljrH5k/38tbaXNn3a9utLr2","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":741877,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhs6PqCRA9TVsSAnZWagAAAOgP/2l/xG6behUrFhzGBRMz\nrxP+rQZ2Y1Gljy6jbYKA2zD4RcaCkvedM1fxuD7hc/w/6IhvSRTY6ifjiuGy\n5ecTSD+/6ylXjkbWGlVs4fkQRnKeXvMwkYlX2HCmVWP7xeol01VbREIdykB5\n+HfRn4Y1WFgCm/gkPtKbgfIfKq4RpXZlNXt1zUeotVd4HoE3OKbmUJ4JUIEN\nqVjh9aDfBp7Mvk7mUkfm6svezJWOifrOFxQkIlZBYc2fUwg/ybI+kmDBg8N8\neWzjRGFZUVuiPQxJ7UlVEnAqmBWZjl2Go/8Dv917zKxwQuDiDpJVDNgBw+22\n0dvUVYm0bU2bFGFCe0LolJUMJ/hzIBc2S6S09CgnUgZaFR5b1UEbhUf+6kOM\nZTFjleKk/e9rNGWV2J6YffGtAnuVlCF9QFPQB6McO2gVHRuympxtEfeYieOj\niu2WN7umyBnIpgepc4qW7xwhRRFkWZTK+BIXM/XoWxwFNNNAXXp04x3cM1qm\ngX7wrbN6ltwseqfMf+eBzkWMYHLPnuTk+clquFTmjXIEMP1Cgh4yTBQZpFJ9\nCNKCQm0XzRLv2ucJ8WAXESSsR8lUTCl6GAnN6L+259EOkiUUiBRkS16czsLr\naPSn2TzJaMPtSmE0uzGcOuhEzuw5wa+JHe7UOqZ8uWcsdDS19tOC/TkHK2JB\np4la\r\n=MLd8\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.53.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"12.22.7","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.53.0","@fluidframework/counter":"^0.53.0","@fluidframework/aqueduct":"^0.53.0","@fluidframework/sequence":"^0.53.0","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.53.0","@fluidframework/datastore-definitions":"^0.53.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^12.19.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.53.0_1639162858081_0.9479534932353171","host":"s3://npm-registry-packages"}},"0.54.0-47413":{"name":"@fluid-experimental/react","version":"0.54.0-47413","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.54.0-47413","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"1091872047fe2e0d81a311ef8c3eff203ae563a2","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.54.0-47413.tgz","fileCount":231,"integrity":"sha512-yQ5QwmUUA64TE72XyEhwPm3Dnj36dV99Wus7hHE/dFq2iZZ9eOHAL/KtJqhKIQ+dtXydkvXheXAXxmpXPYTmOA==","signatures":[{"sig":"MEQCIEsN8Mk5+S/RuIxKTZUf4xG2iDv60Z7ZXT8r2FOwfuVmAiAx7lghJfNmJwEQ0Zvckvu3aELCuWoE2ymRD39G7VGhLA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689573,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhzK0hCRA9TVsSAnZWagAA5CIQAI3fax7pwQMAR4lXXs4O\nETF2bZyccLzgIhQkLX26ogmdYwiALyAsbSX7VuBguLznxykCU0kKG03Sz8RR\n2UtYPG+ih04W/8DVU3db6m7yBRuizcuLLjDP6ZQ9vColy/VFxHp8aFPk03r5\nUfir1z3vY9a7YqZQqMfJDkHLU0qCssZBTEZmLbQeGsH6k2pkf+oI7MyaxyOq\nPf1meXnCa8FQXldk1XIannXkgGGScxdkAsjlDtir8FTbxgsk0oryAsT3uogv\nWuJxMYIU7jzMlOeQkzIzIjquSrMDMJGXZ+0xX9PAAknFXSC0ukNdBGA/0oar\nuTR8YXpp/97s0DcPvs5tzikaDvqm4WYn4+yfHIxeA5caDB7WOC/9GToo6ECp\nvXEii1AavoX7R/Crnf34PiohsHPfmTtXtxGaozEQfgs4VW/90QbxrXgX6etC\nWZf5RmIHBalogS0sNKOTMUhaLgulVE7Cu5Z8sQoHaSXTUlI5bo57aMvLb7mP\nmcgOzJ2YKPsQsj1gjSKbcZtqppPmMto1rj4f6qd1H6e1uIwEPR46u5bwiEG2\nZyO2kWHGgpYVRYYQhtBbuxBjJWg/f0Gnv7gxiCIhqM16vhXlo37r191uIHKT\n3wbt/i8nndcE54+p12Jvfrr9A7jZM8Rii7d2nL679pe4aFudKpCXYoM0aWxf\neiGT\r\n=FxIg\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.54.0-47413.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.18.2","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.54.0-47413","@fluidframework/counter":"0.54.0-47413","@fluidframework/aqueduct":"0.54.0-47413","@fluidframework/sequence":"0.54.0-47413","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.54.0-47413","@fluidframework/datastore-definitions":"0.54.0-47413"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.54.0-47413_1640803617560_0.6509070115765554","host":"s3://npm-registry-packages"}},"0.54.0":{"name":"@fluid-experimental/react","version":"0.54.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.54.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"c27982d45637fe2bde8faf5f79f87346ce2146fc","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.54.0.tgz","fileCount":231,"integrity":"sha512-TZroH/o99yjLDrus9pOxtxDDFYkGoxZPToWDxo3w6S4IqACSUUzcto2f9bAe64kDezzpcGQN2uXcqWEhTC0HbQ==","signatures":[{"sig":"MEQCIGbskAWcOeAVsFkR77czuhirZ7X54YzDwfluQ5DdUeINAiBNvP0Y0ightqlFndFqK/tJ01SLFT5PPA8X4YtifmzzNA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689537,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh03plCRA9TVsSAnZWagAAT/4QAIciOqUQE6L1krgHppL1\no6W17SvlgGcw4n2wgAvSNQJMwxaMMFXEtuOBOpu7O9kzyBBTPtBMvoUpoBvl\n69j1SbTlf9lf8NRC4VwPBvNVUDicROtTvUEZrF9rHjwvq3fV/ptiP6IZybOM\nrUn0WPCVt93feJWAstqb9TTqhwLMw6KXcBiDM4r6CfIYSqqgisFP38xZ9cXt\nd0NYmoKdF63GCcrBfdFVwU+AkZqaWFW3Gy6mJ8pjaaZdjCtW6EpHLuMUTMSN\nrjt7hLsuIrJQqUzW6A+fdS+uuvHEZSNmNOKQwqXi7tGRjmXsV4jABhh4RURz\nmziZVNoKYwdENiiPK+cPZEGEvXYwiLeIcxynlXJqoJNiIWRRQ0Ru8Lzrbg1/\nLbuq/0FpQCWjOopIQeOtGXE6tCluIfjWXVqJ4RT1xXhHuLgI3BkNSUd045RN\nykjallrfV2hbY89+DGGYa4httDwYDCU7bhitvHj5DIaGWcCDgaomw36L3Y7o\nLDmJk21g5DM80Xiq2rUh9QRTPEJ1+tf//efrtDm9l47/YWEOHUy12YIylN5V\nYi61l00Ades5nlyZY+Tne0VX1Isz/uw6/R1FgxZEtWqV+GcIcU8jE0Yhx4/A\nX6paU/rl1CSp9ulSAEQV9ezn3Tn1eAOEkEAmiLzVLSJT+HV7jDini/hBdRrk\nnno1\r\n=mqu9\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.54.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.18.2","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.54.0","@fluidframework/counter":"^0.54.0","@fluidframework/aqueduct":"^0.54.0","@fluidframework/sequence":"^0.54.0","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.54.0","@fluidframework/datastore-definitions":"^0.54.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.54.0_1641249380951_0.0458578840111028","host":"s3://npm-registry-packages"}},"0.54.1":{"name":"@fluid-experimental/react","version":"0.54.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.54.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"9eb3ad42e529726f4dd887cd50baf10d8cc64cd7","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.54.1.tgz","fileCount":231,"integrity":"sha512-bF11RFTZER32zY1+nXOT7Xj2vyYM1/0r7uxsdBv0hN9g8c3XsQ/xk64MAEQDJyTzFJEgdbXHLUSO1l88P0qUcg==","signatures":[{"sig":"MEUCIQCMZocYkjEq2ZZL01po0Sfvyf+ZJqv+IvRP81ZxelvjEwIgH7XXgRX/cqkzz9oFpj6vsuZ2fIn8dW3A0LB2Vqm92tI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689537,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh1hWuCRA9TVsSAnZWagAATwAP/3eMUMw29Rn68vnn+yAk\nyuxpW+7YnQsuu8aoCTiykYbpvzx2+ZyKuy8/S+CjfXN4bqiTS7lECU7kOZPP\nbpc+h+3Rr4Hv3cJ3TTlw1zAcnNfQaGmlukySiaNogSMOIgTGCwC7+FL3BZ29\nPUnzT8vmdlDPI6UqCFfxmKctylcKo3HG9fMGpc7VtmiDkeHR3vB3tjwOnL/7\nzXDtIIpImz5yQMvE9eW+6htP3rf6BXjKdbLWdYGaptTFdX4mgMYdtKagjJ+x\ngquotNiGAr3o9jqZp6TcJHA297O0GeHyUg3ypgxs4k9ANXSjYxKPTo03XBri\nFXIHZO4yVUOk6wcaANFnqvjTJCvABB54BWnj1KeJbzgHEHlfjX17Bxl9akOv\nuRYY+/4xF1A5LJyBUU1qVQgJdzH+5j6uBT9HcTJOvwBoEw5thnZ/Td9gTrnx\n7ENWV6O1N15dISXCiTtv448ZoBa2xYo8kGxkfp6BQic3iRk0ZMAZZhecWs3j\niPwvhEGKG/tgUhmmbe3xNh86Sg5kf2bb0Z6F/xPpSOiFbkl0OfW5UGCYMH4+\n0s0h5z4iMNzxP9g1FnIskV9XAOaF+VKwrX/BDFwz7W/5vk5tMsYiXj66xEBv\naMlp8t8iS1sqy5Ew5lmLQmRr6kCLF+k+oEGpgZTzG3RuLt4FwzJa4lrFxLLm\nR1pB\r\n=UsQp\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.54.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.18.2","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.54.1","@fluidframework/counter":"^0.54.1","@fluidframework/aqueduct":"^0.54.1","@fluidframework/sequence":"^0.54.1","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.54.1","@fluidframework/datastore-definitions":"^0.54.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.54.1_1641420206049_0.872022522507766","host":"s3://npm-registry-packages"}},"0.54.2":{"name":"@fluid-experimental/react","version":"0.54.2","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.54.2","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"90923308b9b6925f7f780d23421b8a91922554c0","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.54.2.tgz","fileCount":231,"integrity":"sha512-GFUSk70qDsuK7lmIZfLeKpwFHRcgC/X4KXN4mxIx0t9tdpJYUIWdvQNUGY9KXVfWmATJzkTlB2jOTf7GYrx+WQ==","signatures":[{"sig":"MEQCIDyKjEjjO/+vdPnVC6jo5hKOUWKAOERBjzrhErJBRMkLAiBwD+DpmtQZkhd4m04WDzyCN697yGT+MReU3IDvsWKErA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689537,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh3e2XCRA9TVsSAnZWagAAVKMQAJUhBsFZrvfG4l7/msv0\n5ix0XUMYYwwhaJNq/ldADFfwA7i0soAP8cLLmAsf7gIBhFE86x7t+v9H3nqF\nmiWU9xfaVP5IG6Ehe+rUn4AgGCOsONK80TqNW8XOlLPuzz930NpZSh4DtQR8\nDmnDYkIehVoKdwUpAv+B89yAb9s0ZhEjKyDTUc9Df3n0c9SijW2AsvxFzmB4\nVZcce08HVDsI7igswIVzEhKtbizQ0M7Cz/AqXKRWhXaotF/KGahZtvn8aN3C\n1YKVceXeASWAoropOqQH6JjSv3sTzVFuN0w0Ybg8Pn0Sr6s7k1WEWS9mafDt\nezi9JSvIo5qXNJRz+gWtNJN7mszlF1Z8D0d4sGk7mGVrF7AZt/WVNHjucRz9\n/yTnn1kY3h5uGB1OxYmRihzH77tIjm4exYsfhnaKlTe906KkOjfrhJLWW6pK\n6UT/mM/wrLiIU1Tc0zZK0H9AhxWhEmoYoj+6dui1q+ctcWTyFNbLjm31NHku\naXbN4kTVZ/k7dVNFsTz1Z5yvRL/icQsFhyEAnm/Pb3e+dJumE1tD9rgcYYtr\nv/zONNMKpoNyqNwywOQBQSJwQgfVkN5fDkr6DDp6jJ6rakSwtdOWQkh9//gT\nBPbukECJhr4OzwI0hTQcrOAd4Qi4ZMtsav/lLV4qzp3APnMZv8VhvkQnw4iX\nRivH\r\n=e4b9\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.54.2.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.18.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.54.2","@fluidframework/counter":"^0.54.2","@fluidframework/aqueduct":"^0.54.2","@fluidframework/sequence":"^0.54.2","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.54.2","@fluidframework/datastore-definitions":"^0.54.2"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.54.2_1641934231207_0.509111479058574","host":"s3://npm-registry-packages"}},"0.55.0-48551":{"name":"@fluid-experimental/react","version":"0.55.0-48551","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.55.0-48551","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"3ec83f838aeb547c28d9e9d0b92a5f4d93c19861","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.55.0-48551.tgz","fileCount":231,"integrity":"sha512-0xkMR4OxgGP8cdzahIV6LCr429U29yl4IOXaiRSM0A2Ia0blM+lf5LnUUi7zoX9kTPMK5mCzddxAnmQhfMLZfA==","signatures":[{"sig":"MEYCIQDs7tS7dQezZaqsZJGhPh0fd80f+oNlSBG6q5+D1ehDtAIhAITWzuSo1o2lNvjZQnHzIuVoj/OtEOkYuOUWQriran9W","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689643,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh4eqsCRA9TVsSAnZWagAAwtMQAKKKPjl1R5R0J8gZwAgW\nxaNgik0Y7wEAMxWcIgU8LNxqOdprILAyJ/Sq0Iu/m5O0Epe8lJAi4UZ8006J\nMF+tGXSxRLSdfSJjyz4sGe8HpjoeDH9ZZ4HnViLt+TT9cAHpZtcClCxTY8Dz\nB4Dw83iv+8pGYg+epmwuV+Qh939+cpNk7wReeDfTffVVIH2Rdc9/rsZiaNGk\nLaVRLP+H7lxdlgzsLIMCstNyr/Jtb5iVDNvvHfZ0p4MZcUzmfqKWnam1ohMA\n8889IbaLCekHKQger18Ih3zzJJXadpe7RiMOPXqUFDumyaDqMVqbNV+juZLn\nNSo9vCppMI2PjY+EpC0aviZoQX4XB0KLLYJbWpuErKEtZOOr3YKXF7cyW7lt\nvhhkk36op3Ay6fxjdJJrHlWkHyJwBFOdM292vuUk2W34GZM65K2HQkVyae1K\nXitXW62pwEsggY1DrZSe3MnK1xidnYzl/dUjkAJ9HVMNKhOlBnkhUKEoBHls\nbobTyyDM/m4OLbi3irCl+vFgUiOzDal0A3Ndp3pa0VG1JT9GfddRKPeE1wid\nyjLDjqDA7mye36bDrjxbk4C6+qEmjOxTLhblovSaWN55u60VKz7qBLERIJpA\nT3Eff9Rd9KBDP/Jmtmn2mClUIMZVV6Di7NZZ2yJ5j1i2H1pY4ZQAAxEakSf4\nQ+k5\r\n=kkah\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.55.0-48551.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.18.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.55.0-48551","@fluidframework/counter":"0.55.0-48551","@fluidframework/aqueduct":"0.55.0-48551","@fluidframework/sequence":"0.55.0-48551","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.55.0-48551","@fluidframework/datastore-definitions":"0.55.0-48551"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0-0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.55.0-48551_1642195628438_0.27057750707418315","host":"s3://npm-registry-packages"}},"0.55.0":{"name":"@fluid-experimental/react","version":"0.55.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.55.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"7049fb957643ba9bb9bbc42bdd24789da6bc11ca","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.55.0.tgz","fileCount":231,"integrity":"sha512-/FjKqLT79+F7a9gR7Uf8jTU7ASsNI7RxJpo+yT5v7WdmgCxpPAzdd6P2IafTdPdmepRJ7B6kHhme1uJ1cnsmVg==","signatures":[{"sig":"MEYCIQDret9PaEFsNmopxm7KHmOhj+qJoa0GAWRqjgPgzaJgegIhAKi0s3OOYE9xVyYwJouEt3zBq2BohRuWhZNg0Rvdz3wH","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh51CCCRA9TVsSAnZWagAAfZAP/1lqXT5uVrVYbunb6M19\n9PMGaCwZQKmcSgWU0ygl1ykmNmvoZheHc2s99VakZK+RMVMRF7dWnRJke/gk\ny5xQl3ujUiONJVzeXLz/pwAxEMudQq3Qk6I21+9vqGCgocfpiH9CPv54Orv8\nBJeMbvVFu0WjTDR8QHxDwLDA0P7HB/W5uIPWE80VmoXlZQepZE5XJkDwMWkP\n8E+Qzj7UsCmCqXkYKbUYo/GUa0d+lJx2taXRkj3uyBL8URwdWPDrtQRiCPCu\n9mM5CLJmdfe13EGxYTz27x1GCMW7Qs3HUipQRc0972uWcKueZ4SbUQ/x9FbS\ndV8/JuFNlCl+sGWQtp1l9wPJOuGHYqa3/gknfn0447gj7LfPy1DkPw4yjY5+\nCFGk5xnXR8oe03sXNsr788KFneXhyo+K9NAvNFmKM+3Cl2ehiK1Sf77omZuX\nAfYnqJsuKa3Wg0u0daOvoIWhg2oShxOXkWbfxY0oYBGMX13PETWMPB8ltv3o\ntniDHJ3rbUnkOngo2GaAUMA2Az0qcqMHAvgcJyS1Xdq7K7pE1h9FkMyWRUjf\n/7L+KslhARD6IX9IUGR/QTL5u7026BWtUkKmPuujD66xOhbTumgP4fE8ypmT\nFbfbV1irJfe9a4H54x78RTfEH4PL+DfMLyFYetCjVXlmYeQ2OZIVWyABt0Kk\nYrIx\r\n=eNiq\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.55.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.18.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.55.0","@fluidframework/counter":"^0.55.0","@fluidframework/aqueduct":"^0.55.0","@fluidframework/sequence":"^0.55.0","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.55.0","@fluidframework/datastore-definitions":"^0.55.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.55.0_1642549378612_0.6855252396140259","host":"s3://npm-registry-packages"}},"0.55.1":{"name":"@fluid-experimental/react","version":"0.55.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.55.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"d7ceb99076e0057f38ea3865dad2c102bd5e68d6","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.55.1.tgz","fileCount":231,"integrity":"sha512-lZlUVSLdKo6leHQ7vTvYONOo5nvin70dwAa5m869ZGpYnAZmxV0LyxIufScwMBqP3c17M2N3TsFuI42Kx7uOgQ==","signatures":[{"sig":"MEQCIAnjKqE9ffbPKmeX/RJSe57M0eRDRPS4J94oW0LEyzJoAiBNENHqO4qyXdwBTET7IUWSERIx+DNtLZuSweNkmPWtxg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh9D5DCRA9TVsSAnZWagAAAagQAJ4KC2i7G6tQNt1tcX/8\ny9PatTE/yky+Tm1fdGlImaIcx0RFApyOrbhMjkoijd+z3kxVv4zEWry7ljsr\nhfa6bvfuAqf0s3FEfdcI0p8xL35xt+RaRO3ZU5cgwLIjcY1FXiGrJ83y8WOR\nWhNXUmkmzc4bITqAHrsArL4seWStwh6zHIX5f06L/3JAmjrMUWfBAji4Nm4+\n8Ze4GoEi7tj78yJYIV7jE9Hy6qN082ilDCYSwdEQ2sibyZPYS8JMNR4gjrFu\nGd8TSdCJV7nd8OMhKUahe5Kaa+pb85JsKckFXJqhq2dZEnC3xnEKacjlCgvR\nz5O1FfXp5eKBlWvleLsaliwEHv1hBNDV/uEA9XRRKTTV0mfby6YNGEKQgs/W\nSIKgU+11mxg0XHSTGQnYUhVd6fT6bm9ccPtZD0wCkyL0kZku+pjh2X0ddALq\nOfCDjmhycVU/oUtN7fPFcARB+woRTeVcHsjRAaT08UA5iViMqMsDKOcmnTwp\nz7KElzOf7UkIznmTKgjaXJ2tzoptpKVB2KjGklStzPmx86vYFiXN7aquBXHO\ncW/NVmNc2xXySjgt0J/sRg++8f24fnJszG1xYY+UivjJ7LDOmAe3G6kV8BiA\nLJLwobZzPMVpA4fl9UyLzU9DcXoX2QBZEwM3d0Ff6kmaZCbg7nukCKkidV23\nPB0E\r\n=HcGU\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.55.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.18.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.55.1","@fluidframework/counter":"^0.55.1","@fluidframework/aqueduct":"^0.55.1","@fluidframework/sequence":"^0.55.1","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.55.1","@fluidframework/datastore-definitions":"^0.55.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.55.1_1643396675108_0.4270646460299259","host":"s3://npm-registry-packages"}},"0.56.0-49831":{"name":"@fluid-experimental/react","version":"0.56.0-49831","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.0-49831","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"9ff7ed904a7582057b45c56ff0cfca20ca9ad9d9","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.0-49831.tgz","fileCount":231,"integrity":"sha512-lNEknVFvnJ71oCUJLRbTsD8kN59ILq9zoJB0aIyuaDpdd8sdTVPTqeu4kSjQpiOxiD0CxsZVJqktH/I/cmivQw==","signatures":[{"sig":"MEQCIA+sC8TYAQS6fTrsEP+MSHJ0DBxJ1QRWRiYhE3PfR820AiBkQQJmE7znO6OEs40xXkj54kZNbCYAadqmvmyIAgufdg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689643,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh9FLDCRA9TVsSAnZWagAAZAcP/RXa19tY5Tzj3j6TT37z\nSqvcbIUiSJhpZNKNFQMz+LTMR20+ZxpdsOuenZ5s+SaaKsW7MiHVaYgGC7ab\nf1zzL0+te7uc8mVRRMlaKB3px3yY0FrA3Xad/u0RtAvNF4zM9UvPTAxhGMkk\nXIxKzRyL93mQ/iRH03VS573AbtTgIh3OWCqhpVRcHhIHUeI3H8lYdVqQ5xOB\ndYReqa9aqnOd7hufgz8VsFOPh1uYuVrFI1j3fZBKTC9Uj/dVsoevr6Z0O/VX\n016FTxrFGqaw56sTPrAqPB+fRjH8VKyJ+sLdKxMtZRGgVDhPwgUr3itRhk3+\nDLwbSkGZo9ZkLcGOdaE8SKlK8Gta5i7zRU31CMX8Isqmy0TGeEgNc5KMDyXo\nkzQLsgDVuXahLNzLkWJDHuA3OrkFKz91aPiKF5ce6nJYlkIaZq/AKyFFv09K\nAe1Mdpx8E9pN6jSI0+c5KTHeCBmEwqf3WiHTBY/i3lf6ZH0iakPlfb8t2ztV\nCDbw/jB2/UwlokqeswkJwNmNLK2unxBB+47cWQDnIu/mb0VEmiYWoAK9w9sl\nfT5XQzTfgvrFgkD6wAtTdxDiweqqiJlobqowjXmwmkQKM/Ao+HtQYAEduN8v\nWfhJXq+Ug0o5QYz9PXH5mIuAa5oleeqLmiR9PCdtFUedkx6Od3mmX3WKcwWz\nOkpi\r\n=NUYi\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.0-49831.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.18.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.56.0-49831","@fluidframework/counter":"0.56.0-49831","@fluidframework/aqueduct":"0.56.0-49831","@fluidframework/sequence":"0.56.0-49831","@fluidframework/core-interfaces":"^0.42.0-0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.56.0-49831","@fluidframework/datastore-definitions":"0.56.0-49831"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.0-49831_1643401923816_0.7110657750862039","host":"s3://npm-registry-packages"}},"0.56.0":{"name":"@fluid-experimental/react","version":"0.56.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"b684090a274ac3977a75a868acc35a4ce39db23a","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.0.tgz","fileCount":231,"integrity":"sha512-H7cPkjJo1uKLtlvpEpUuY5r/St4PLuwXKZZ8hDiTpJ0O9RcaeC7XTr3Ny4gsUCbLevpkMsRG72HbYDyF6DCJQA==","signatures":[{"sig":"MEUCIQD5Jb+rFmzJk/Z0gaO83ySocvk2sBMI8jj31sXosIrfbwIgEP/fG99mQy6a/Fs0WGBfcO8S40gozY9WXjKGE4dXsDU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh9IrTCRA9TVsSAnZWagAAyyYP/3IwwI3IWyu8df+1rKrF\nSJZimEz3tZS/V/2TmdIlJo8VfIHb8BZ1NLx0nxH3TVhGBNaTYC2Ha5F+NEkd\n9UkI1FL8cJzxXg9tdh6udY8C6Xp4nxt8k37ut2ROu0WqnUueNf1PG34YYZJq\nV3JqmiDjPnk8SbxJdFysQPizRvPltdZbUBKf4dgAfdfIg+Es1I6UVdkZHWks\nAnvRJJ5j+WDKIJIFin43REl1jpF2A0BQeKNMemU7Av+qxsJWcEZY9O/na1/5\nACeh9UDryAy4OyjTlPw+1KGgc0OFfUZyLhuWf21NY6SCkHtFiG4CPHyj+Bxy\n//NQ8jtb6pI+PNlZUBEcqm03Wf7Sa2SzflLZF5KabCAoO8g5AO5fuVBxuF/n\nxNI4csm1OnmbevRaCsuxmzYB/k0Q1YM7lk69Rg9ZPaVVqVoQ8QvwZNA3gJW5\nBy5OJzwOxwAutfTOj1R2JUeZKw68lbiv6YyaODgCF3yWPn9mVq868bw2KkjT\nDCyxvAff3iH13qCKMCS63QfVm3a8c2nuqGB2s9eCEwYt+9f0GP7eV291okhT\nIwQnIhw/+4otKQ6xEkSW95VZtqa1iCfkJ44WUQb0jUrJYECdCv5/QKvG0s2c\nyrlYks6Oz9/nmaF6u/0++/ZMOco1FOOEt/RZVVGpjz4GI0khRRMiC99rS1bq\nuMNH\r\n=FQ7V\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.15","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.18.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.56.0","@fluidframework/counter":"^0.56.0","@fluidframework/aqueduct":"^0.56.0","@fluidframework/sequence":"^0.56.0","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.56.0","@fluidframework/datastore-definitions":"^0.56.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.0_1643416275371_0.06432525991802862","host":"s3://npm-registry-packages"}},"0.54.3":{"name":"@fluid-experimental/react","version":"0.54.3","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.54.3","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"1fa21cafaeca46ef59ae70cc985cfe4b256a464e","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.54.3.tgz","fileCount":231,"integrity":"sha512-hKaZnYCblnpXiiQHh+KPQ4pZTU79iDQ4W5EZhG97UzwLHzoNtyrT4ERXu5rKCqzN2345EPgrUEU/9UDE5DWGzQ==","signatures":[{"sig":"MEUCIQDU0o6s5AcQ43msjFuSI8oIir6rA4DsSCpuAV4niLX3XgIgXMsrGEalYftdnJOfegxpNgxrJvqLXZpFocRe35yfJGg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689537,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh+zuTCRA9TVsSAnZWagAAtHwQAIoA2PQKZ4ZYJBQKBveq\n11piFH1o1iB6HKYblvUjtzPbfcLufkbmxYXYxtes8/L7KbgyioRQQRxx7c+Q\n/yQVqJRpIA9xFnZ4GBcjvNbr9eh8IuB6L2AkqukB/p430Ge+zYnN8rr3xlWU\nhPWZiWqKfZNdMg+NfKtAhZhHvvIO4/5zvvD0OnhChVOIW+NmlUy3TpCWBqMY\nTe24hK6Pcm2bpLOalndRkkqhZr8ga4eUHxpXIR4uBQYPfhjqYUdO+UqU+cA8\ncgLTeYJGqyJtp8syHIuTJM6Up2G9fWp/t49ehDNbtpycpm36ZmX2zmS6cs46\noTWs273idCEmARHPStf7fUCeV3oVVnOS+B4Cuw5X4uKOzPCz5E7hC8Wz0o1q\nITgBh9qnmmOB4lStPnMDhg5SgwesSi2heBsb+ZTcpjFPGUU8Qq63MmBSOa0c\n9inneATXUGO/7fWlqcwcPUtuGOPeAVmv8r/ML6/bQyVPhmVZeaMU1cJZNpn6\nJz6CBLnnIMQ5Pz7tOfvB35ui1T/fDOnScKE9ajF9awyjeIJHGWTxrqBBbCSH\nQCGeBRZV3jbNmO6vi10l5ZK80REcNDwf5LbAS0+/fmHAhgZ7LaVzOtIg/hGL\nstNNmM54OnKqJNEJEHx6rqlMPQOfHnlMN/mO+JCLecRh9MfuN3AK0kr1nqr7\nKdb8\r\n=9Qro\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.54.3.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.54.3","@fluidframework/counter":"^0.54.3","@fluidframework/aqueduct":"^0.54.3","@fluidframework/sequence":"^0.54.3","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.54.3","@fluidframework/datastore-definitions":"^0.54.3"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~7.18.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.22.0","eslint-plugin-import":"~2.22.1","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~26.0.1","@microsoft/api-extractor":"^7.16.1","@typescript-eslint/parser":"~4.14.0","eslint-plugin-prefer-arrow":"~1.2.2","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~4.14.0","@fluidframework/eslint-config-fluid":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.54.3_1643854738898_0.31438797027458176","host":"s3://npm-registry-packages"}},"0.56.1":{"name":"@fluid-experimental/react","version":"0.56.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"5533fcb6193c31d2b06f6b23137d5cc46d9f4f3b","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.1.tgz","fileCount":231,"integrity":"sha512-1WmpA4EfTE5WrKxj/rGbeXaHVFWngbRUUkdnIyzuUQOZ6qalD5tii9K+1jpVuYohI3VAnL8ItUZz8iwX9YOWDw==","signatures":[{"sig":"MEUCIEYkmUH0Plc6tguaay35P4V8zWx/sqxsYvfzrq/OeT5bAiEA5EeoBDw7JWcXMIY8nlay2lPns/C/dY0C3YaEi81Rrvs=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh+z7ZCRA9TVsSAnZWagAAsNQP/RxAPeNcfFuF8v90pT/h\nLlkFbc+VEDZN7IDJxK//ArOgmRnIb2pU89+SXfSwrZ5PVhE6pWlN1Q9lVBoC\n4L38xwxk9+XIAzytKlN3/qV7EWfIk+vP/XqozerWzj8zR2VtK7l1+ODqerYe\n5PAvG+5Ks0nU3OOZIEw06Vo9zvuMD6eelkADLzgyDTdRgfv6lMYxySaMQ+MU\nrMG/O+aIEGmKhNq6TIc9DPIMnfVDTN7ZeZwirl0o3tvGP8ZZl0boshKS3lww\nnBlp0ET1X6vQiWIaiSIODIN+y6ca1UYTVfjeo/wMKzX5ayNlRUvg1RgXdLv2\nckVM0Y+3aqM5WrxcFJWHFPC4eEj8HEWOCMZ5RefM8wRhkanNBNZ0j8wYdVgd\nYCv4WbdgJRC2+a0SQbNgr4B4npwyJ7n7KePlZUE9ZXTO4LtF7od7R8W9W0yl\nKefpbQUqUSzURwQXl/UBu3rEO6b0lRZo7JJ3iStF0cu1QF5sqlkT7FFIiwTQ\nAxOCOFOM7X7Wxd63kTz14PpfSqG+Q0WausSOC7AZ/Fjy9su7x/wOQVXBCtEZ\n+Ri8Wg2qfWFapWX2gLAX6/aVTdx8d69ZkVn81+RGbcD88jJjW9L2BsDyP5m6\n4Peb3457dUf5tGbLrIWWSaUAq3StOAKcN0AL8ZmxAV2vJFIg+4uJLMne3qjG\npg+8\r\n=GTaU\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.56.1","@fluidframework/counter":"^0.56.1","@fluidframework/aqueduct":"^0.56.1","@fluidframework/sequence":"^0.56.1","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.56.1","@fluidframework/datastore-definitions":"^0.56.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.1_1643855577609_0.08358043045164698","host":"s3://npm-registry-packages"}},"0.55.2":{"name":"@fluid-experimental/react","version":"0.55.2","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.55.2","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"},{"name":"danroney","email":"daroney@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"2fe5750872d4b16f3aa532527fd7765a9fe8c7a7","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.55.2.tgz","fileCount":231,"integrity":"sha512-rtRCwfT92OTvZ+MNs3b7RkBCffAe9FBqgToPQSKRhepO7tm8JfuxNoN6YJsEQofLCFDUa2ZjeyGPKMmw3iIAIA==","signatures":[{"sig":"MEYCIQCsUdy0pVyZXzuxZBlLGN144HpD0j/FyYQcnl5/QaKp4wIhAN2YxD/FMqlKXRxHLNo8xl44dllQOgTQ2yd/B3Es4UCI","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh+z8aCRA9TVsSAnZWagAAZGcP/0w6biWadh2L/MSQiFO8\n2z1lc5x+LAzoiTxqVuU3HRkx1C2W+AsbJzlIc/IdpJnHMijl/nfjhO3xw6M8\nplwf/Z5xdOXy1ODjFP5yElvoma23Ll24wvWqIU0R6Tyr/tARf0wIs9ke+/Hm\nzRDltF1TlJuNvIdZszw5sxlBNwx/3a2ATm4AD6TfHhqt/p7IPjXHXaH4L52P\n+7/9IyYZQfIPo65o/6+CUpW93hkELWcuSQ1pJ8o3v4tNcmm3m4MkjKlGdUB3\na8Z3jL5NMbcyS4WgT+EJh0MXqvMoDdZrSXtfhRnDRymFLb0HJvln1mFo350j\nyHsByoh7dHw/V7gI1maa6K9ZvgIEAL6o4dDb3rTP9bQPypkUmMI0f44QBwKN\nFn16l86ZzNkyOXdaLQNjHMx7dqig6qb0+U3HinCnxzz121S4Y7S1hLV/BpmE\nHMSIPZV5zU68m0DA0zCzq151EEr2be75bxuXB1XNQEwRaLtLkeLI5ll4d6BR\nlIP9nzGv7hua30E3hTroPs05Rde6nKv0WoifoqL78EW5NCcR3dzferB0Hjh5\nqAmV6qkDqW8JQPAAmRB3bu6zrfsQ1J3i5CtXkXVqZJpT3rFak2pd+eIr9wBR\noOtti/yZ1YRfiRXTfUOR/fx6TUarUmooSGw+uvAcC3fnR2muzmi0tS7amrJ7\ntJQ8\r\n=VdFb\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.55.2.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.55.2","@fluidframework/counter":"^0.55.2","@fluidframework/aqueduct":"^0.55.2","@fluidframework/sequence":"^0.55.2","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.55.2","@fluidframework/datastore-definitions":"^0.55.2"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.55.2_1643855642141_0.7555183415465492","host":"s3://npm-registry-packages"}},"0.56.2":{"name":"@fluid-experimental/react","version":"0.56.2","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.2","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"df982681ce4cd27e3356d781524d29615505048d","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.2.tgz","fileCount":231,"integrity":"sha512-XfC5Hn+qdqeVR3i0hCLJqijzQ29LVJpKxhMmR9BslshXvDGwYvhH6epuWHI6zJdWRDMGdmgO9EQOvqieTe9ubw==","signatures":[{"sig":"MEUCIQCs2qpgesPtJiTLwkx8cxWY97Oyfa3dlvVBZ1xQcTJdRAIgD3jexJkCYmvjOYGmVDrds7lm83pe19otXGZrxPcd+u4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJiBFTtCRA9TVsSAnZWagAA1zMQAJwhiQcyFIHGbzT/FcUq\n+VctQOTd928i2uwVcwcQObu2fLds5av8tCPavEGEWJoY2URYCcipy4/arvYH\nQSABM+V+ANDK4WlD3iLq0dK4n2SwoiiYIkdzHNGCaHFB/cTz2WZtzplZ9i2k\nASzTOMY2ObNbJu0xXwD/7QiJbbwPQkxTHPif29ZpJJ464oEZGWpuRGs9ngbv\n/o3TR/pVscQ6lgPJzFEenHYAHvR8uU/319jV6BgWdZe89RXnfN0fUU3wMNzC\nyhvVrwoAYK7hnvTK6I4T241CBgOExBcAl1qVfLOD0hmV/A/8SbJRBxPhmf6G\neKR9z6urWgvNQIorUJJSA9Y6VQ7dCO4fCked2g8h7zhijfxC/E5U43M1fFo3\nooP6PEj/6MnAIYR3UfFgfRUv2nYUBGbuPTYE3o/K35a//OThOL/Ov54Uli4t\n2vXJ9N16cX5eZNsovwIcxdXZAwe2QCrsch1/aSLbsSg7M/pXA0ODJYMOghuS\naMhixLzHxrI0121rgfjRZ65mv9Nkt6C/naqV8SrgUjgZK+2/P1NZRXAR3gcU\ngh4HI+vodq+pfIkmHIAm5ZnZoyBKWM9py4uQKK+AKH0STpEdzgdihua8X2An\nHormzT/nKf9JbhRhF33wbddUdjG8ig/vIOlGL6GzT+pklveDfoimZFGteik0\nVtMT\r\n=2NgA\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.2.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.56.2","@fluidframework/counter":"^0.56.2","@fluidframework/aqueduct":"^0.56.2","@fluidframework/sequence":"^0.56.2","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.56.2","@fluidframework/datastore-definitions":"^0.56.2"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.2_1644451053308_0.13826754338491876","host":"s3://npm-registry-packages"}},"0.55.3":{"name":"@fluid-experimental/react","version":"0.55.3","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.55.3","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"b234a722236574736d6127556b80dbd6d67be472","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.55.3.tgz","fileCount":231,"integrity":"sha512-5UnAPEvqAXzqTL7+5oBURk6x2IdmLq+xUxI+dhb82H3f/HQqvUI/SP0F5++KR3oF4iAywrazr2VSJrzyi9DK+g==","signatures":[{"sig":"MEUCIQDKSVpzhSFjtO7RttSUYpGPAsNWYowQ3zabDNTxq8JbVAIgU6e3q0a9EK04gt2jHHtvHxu2AxqKG0cUmz7nZZg9ifc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJiBbt8CRA9TVsSAnZWagAApd8P/1KL7lLtU9jpIV3sckPc\nizC1lP2vFkqK4Bf+Bfhtb0zKgfp02X80S+Bh4C9FNdnX4ZuPQhKi6roZPkqb\nh0lVZeRrw7PQyHBosFM951/Gl0a2FZADNgC1CS+TpYmz4pWeCvwBd2/UFapl\nID6Q9N+jc4twMi65Z8zqN7UCWMmljul86ErnlGuH1LP3UIA8U+ulFBcCu9b2\n7ZI5hLmwHavHy1R2nT1QcfuwtulUaZtRCPJjRMM6rx/PUt2XYo8u1uZx3cFy\nrrgqqGEDrGErh6JdsDKvOjhZdr0dRWxCF1Xe7Yq0eV2f9eTL3RwjO0JYva2F\nXLW9RvvS3OWk2Hz0Ix9l0Z8TT2cSF617PBDj3Cmw+HflChhCoGpfE72raziX\n99gybwlk4QeYbhMODHvGgXtFuncRf2b2tIJl7CRkSKVK2VjJerO0cpcDGFSL\nhUj58xlEXsgFid8vO3SksiJLmBwPbI2d0Xyzw2MmwkBbGnmowtjl4c4/Xw07\nCGnyLAUbt7pySpSygg35OqzI89kGo7l5myNOmbzj/byHjYjbiqtNhSr8Odcc\nOM46pcvou7y7TyFLTidEWTPZbVUOOjyu48dek++FxrtFOHuMM5YPRo/u9DJC\nm3TAKWD0odBDlGbd/MxhTOEUyw5lOXvfx6w/kyr/PNE0KEHj8P4NEqOAd74v\nT2sz\r\n=3kaA\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.55.3.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.55.3","@fluidframework/counter":"^0.55.3","@fluidframework/aqueduct":"^0.55.3","@fluidframework/sequence":"^0.55.3","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.55.3","@fluidframework/datastore-definitions":"^0.55.3"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.55.3_1644542844538_0.866801379336861","host":"s3://npm-registry-packages"}},"0.56.3":{"name":"@fluid-experimental/react","version":"0.56.3","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.3","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"25c27de9d8a1a8629bce29de0cc9a51b1d3bb40b","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.3.tgz","fileCount":231,"integrity":"sha512-/MapMmDBfiBe+Cy7XmAOmtCJq0kwVsri4FIFczh2ChiPrmgETSufSglrHMWvlEOSyB7DhHU2Tsu5Ekhogl5LOQ==","signatures":[{"sig":"MEYCIQDtiSwobE7sSPqDn60KOV11+B6jEmSV4HeHLpHXEk0MVgIhAO3atbFInfuU/7eM+NedEljp/VpvJuHXepwthrMPAcqS","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJiBbxYCRA9TVsSAnZWagAAvKAP/251whiZMsZvbdXxn68O\n5UwGMvhyxT899TcxF3TQKxL+7Y0Mc2OAG8rPcTk+tP9Oz3H4W0guPuLqhIVB\n+/MOeBljlLHh64CjeCjDgbpACNyX82F/006w41ZRM8EvJPgjwb+djCMeleOV\nR2cBREZBiwhhe6hBITf+LOYNR/FjRg1kzYkLBfmA4co+DQSPvfOQifsk6HPe\nRlc6sXxDNkZG1JtBscRlVuIfldoiS8e6rL9Oi69VsVxOtnr0X9YaCTsA17ng\nmf07CquzQSskBMCQuv/X8K4OwWN9fplw3UAGcsOB+3beN85MX8nq8BGJXwls\n5b+Di0tL2uBL0zYpDHdt0QMcntOVOXDz08ew59ZkvEDnlemHZt7FGRDe6wT8\ngMmBi7pvN9UCwXC8e3bi5rFZ4JerTKxmYctuchqhrWlSS7S+qzmE/QgKrVKN\nCy+IFdypL5IWuACLV92wzrG+MK6Frl1JqM9kyt+7C9mX9W1tAUhAEQb0Yeos\n3KNgGoffpJ4OsVCBbr2ERFrzysnuKwPNn+xi5K+ARL//BbYR9PZNUeOKSUoj\nXIdQvjB+xqKO1NdyOzTh+xAnL3JKojp8uH+E0QOpAxfpW9cI6XG8FEplurif\nVIEQqRmuAcFfH4Zl7IOmvszsSsvG5uRUaQpb7Dtu+CGQHad4YApjceQgLqm/\nRO0R\r\n=K4Md\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.3.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.56.3","@fluidframework/counter":"^0.56.3","@fluidframework/aqueduct":"^0.56.3","@fluidframework/sequence":"^0.56.3","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.56.3","@fluidframework/datastore-definitions":"^0.56.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.3_1644543064808_0.07049341203047232","host":"s3://npm-registry-packages"}},"0.57.0-51086":{"name":"@fluid-experimental/react","version":"0.57.0-51086","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.57.0-51086","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"56fe613cac47bb166f82424d5b96afcabfb1440e","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.57.0-51086.tgz","fileCount":231,"integrity":"sha512-0gSG+N4r5Eva/ZXtrVqKI2cm7t/5RfKKmcWYLQfjAJ078K73n5EUPLrDeVZnq/pxtIoiDMGIeCtnD7bD5kIMjQ==","signatures":[{"sig":"MEYCIQD0ItKuWtXKuU1DFRVKsJIsb9v+SLY6Bg2QNurh7OitpwIhAKzwwkkCnhq5cukr8qBvRu5a0sJZNHkomNIzzmscFWrM","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689643,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJiBwk2CRA9TVsSAnZWagAAv5QP/ije8Sho+kPiPgXmxVb0\nxBmup0dVLN1SRH+B5uFxYfOZ+c4mD4OG76wovtqZwwHTPDDljV9punyPjczy\nhnYeQtyjPlA8/duwunmUEn2ZQ2doaqkVvLn/TisSl+tyeRqEePW92U3X+upP\n0Tfw812vP2Ngc3c8lJXxSAuyQNEZOn6XIU3PSN3bPQILlnQP50L9eTaDZR2d\nCURzKYEPQUeXZASzyKOk1ngNiMIzVjNCCaMf4HzOS1Nid52jGHJZPfP2q0+F\nj39YDwiZxQ+1op73NNl0V9Eqvzaj1pkBJaB3HyhNFV5mwAJB2bPvVfWsimvU\nRi0xxVE2CywIgIqrAWMs5RXZq2vDsa/G5PAiUtHB9effV3yJw10z1KFice61\nAI0gBG6yJDwmLmYe7CyxPFlvFwT7kBRsH6t5HmOgkOxCKI+LZ0H8wnord7+R\nXJrp8pv01DsC1tBlkc30BZyLFBEX4KcM6cZmDTVRFAYEY5AkSvgWY0Nlg0GR\nxdwF8fdszKVKdxLfhSKAmEAIdrevkOvm83xxkR5B2jv3Blwod0UVjtZdiK+x\nZ+nXe8qWEFKwxBJMTO3sML3JdKSKdVB9rkfYnn7H0vvovtQyDFPjf53Woice\nmjjA6bNI8BFIUsoHimBzyNjMYHL2bwNkwIEgmEsZf+ichBak9oUSc+re41WK\nQ1+K\r\n=Lpmk\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.57.0-51086.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.57.0-51086","@fluidframework/counter":"0.57.0-51086","@fluidframework/aqueduct":"0.57.0-51086","@fluidframework/sequence":"0.57.0-51086","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.57.0-51086","@fluidframework/datastore-definitions":"0.57.0-51086"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.26.0-0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.57.0-51086_1644628277951_0.7444468236524082","host":"s3://npm-registry-packages"}},"0.56.4":{"name":"@fluid-experimental/react","version":"0.56.4","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.4","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"11cb409ff5b4851689235bb34d6b0bdb412fbabc","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.4.tgz","fileCount":231,"integrity":"sha512-A+PbwoWlSs6JV9J5rTndC9PG6cRVdvgMAvSaHZ7/gTnJ4y8CSUr9Xv78dyQGyKdwrKm4HXaMJTeCTdKdB+Htzw==","signatures":[{"sig":"MEUCIQDjBcidJ9ppoyREb9OnBWEJrB4leWGys+fMBfG79XC3VAIgWCf3bw9NPZFOIYn/K8yHFohHyhSnroitAQn0hpaobRk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiDqj3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpo/Q/9EeONJy1O88BFBiHMiHhtkm5/1/IndW8ZLua4KMn84RQP8ymp\r\nUX6utRFVkVTVkMvHMwQox1xH6SRgoxnVCj7UWYXH3v1CLk6wwk4mPm7pqDiR\r\nh5yfMQK0PBzrwQjdlcyW/aiDY6O2qx93HwyCm3DJ2EnAjodzzuzjE20tpEMR\r\njSKhgs2EfCRef9Af/h2LpKtoEQAjFQOa/NXb8ABNVDH9cl+81A8yNA4DarQ/\r\nS1WBTYteTAiYW1slKL/oOTOY+HNTxu+gi4tAikFRToIeYJTZVCTPXqHsiCDT\r\nOIFfq1YqrPe0DNlGp5q5Qa+pxy99qcWR+O6pjax/i1qymafBBEU/jWDTnL7H\r\nSrWu9u08BEVkZGNelH3COrAXvLPt05lgajal+FOxZXa3EUjvRLH9B45K5rfL\r\n3Khxs1nVok64VDk8BR458Vc2FN8l7DUEcnDhGYpJ75XpnlS7jTMwC5swhaS2\r\nCAmCN+7W4SO9Oi4cC1Xfy6JJKSeKAQoCetUGxCuO9+1DxcI/1hBavRnPIqJI\r\nVU4yPotzj83jQdz8PRDg6ypBtDtQIlUsbB5YpU+TNHTHWRUnxinZUdjr+2H7\r\nO71F3URBfisR2eHREX0pS08SSbYUylEVYk241Z85aCjOluZaof+1aq/eZcPr\r\n4Ny81LRudVc5cQqSwYVU4sSPcU2YExSn/rM=\r\n=vwdD\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.4.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.56.4","@fluidframework/counter":"^0.56.4","@fluidframework/aqueduct":"^0.56.4","@fluidframework/sequence":"^0.56.4","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.56.4","@fluidframework/datastore-definitions":"^0.56.4"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.4_1645127927567_0.29181334244704304","host":"s3://npm-registry-packages"}},"0.55.4":{"name":"@fluid-experimental/react","version":"0.55.4","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.55.4","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"d1836d4e179c8eb2e9e3fc89335adb07d33951a0","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.55.4.tgz","fileCount":231,"integrity":"sha512-ifVk9IzxH4S6cJ1De2VS7QT4KW1n1eJfLP4w0qMHcEYXS68ccAQ0jfGS0tAOoolif2OEUYDwHKL9UT5xzhhmCA==","signatures":[{"sig":"MEYCIQDl6+YZGo2In9a/GTlyXylt9AGoGUkHyBffQwW/SVnRbQIhAIYROGwnkTZ8JI/yOoki1IO25iuRacOP+lysUgDiXQci","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiDrqrACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr+BRAAjV8QUBABE0ml8bFnT1qIJhsmautnWkHXGZSKZpWmTU4pqD1Z\r\nWp8pFf0HuiV6IYCsz38M3RfKEZo2DIE+yYi2pZEGoY/B1S/YLhv+gsdWfPcM\r\noJDTp7dr1t/vJEjRBL9xE3hrAumDBwq7VVNOnizIUiMnq2dGSgkYwsxZc+VW\r\nonMsI2bMQTGvF1YYdTqxfrtcluETgoTnHk3GJFy21iMANwyBbwViPhWav3vH\r\nwhe6cXdxz3LW32bBn6fM8ZliBZYjMkZOqQPSYjhXPwgFs3UPNVNPP8wnyKPt\r\nrX/R39/bLS1HnAvJTV2Zp92z1F7BVD59pIfpKjl+6TLsWZfQu+281vHzob/G\r\nVx9MJUfUTeY5FTTH0jzdsTZ+oIFly0iaCQS2zpqSSKBfmtNs7a63bJBi8VI0\r\nJhX+jf8UvRFFSd2pUrZaCEvSCNxgsJlvn/hU0HxaL2KhXaV/+bZLf4WuOKoT\r\nHzMYoHwV0jH1JtQPLp6jYaIFWk4n0SkVTOsTvjwcM0Lf0LcuaFTf1Y5gwim2\r\nL0dFDSulIp83kkfZox6f8pXmI9p0QtQdyaUIgPhclpYwfeBoXwQufYgU1Cbw\r\n/uniTSZpZSdbevSvn137ml6F6ybjeDuw4fd8qlnI0OqeQv8P2uI5qmCf73yL\r\nEEu10b+C0SHjHl21a6R1Z3PyRVwboXgvLAk=\r\n=kUbR\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.55.4.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.55.4","@fluidframework/counter":"^0.55.4","@fluidframework/aqueduct":"^0.55.4","@fluidframework/sequence":"^0.55.4","@fluidframework/core-interfaces":"^0.41.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.55.4","@fluidframework/datastore-definitions":"^0.55.4"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.55.4_1645132459407_0.5774055940375826","host":"s3://npm-registry-packages"}},"0.56.5":{"name":"@fluid-experimental/react","version":"0.56.5","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.5","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"0ef185e518ec1be4396fe2e2dcbf2952ca0f37f4","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.5.tgz","fileCount":231,"integrity":"sha512-IqA65HHaQ1Cv5c1dneQBYMnAR3v+cBu02fxyThu7cdfFVTR883zCEeS5YDqG9QWpz4B2c345hmrLZGQ/fV1O5A==","signatures":[{"sig":"MEUCID39d2ee9sC4tH4gNMVsL9B7LuE2PLrmI/LWK2i2OSBwAiEA8pg5uv6n+m7vKr348TYWn8OAADYt0vEAq6fIUAD/75o=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiEDzGACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqJEg/+KTZNKzcKRnMvp484YASLhRUytlwHavYjRkbukgoHyf8DXLOU\r\np/UbtDhgB6eW5hMmGl+ae9geBJ1lLggio9/n9FSr5+ZkXLWAdH+VUe+crsyd\r\nVRdYhqOpw5dSHGo9WFj3ZKXV9xGEMcag+s0xOh0RLatfK6yey1Osucm1o0Na\r\nMs5vTKebUJeST0B059ey04VdWXBAeaFXUxv0v8bZJPXtdzU4a7qNvK7sp3sv\r\neKPmVqemKZ/EX1ZDYM7K01AU42eTvUge6Of46e08z8NPmlTeVEUrFoFs6COl\r\ndO0Lox8AiP7LJKnX/VaetNL5Ee4xitj1STJ8lobFxfVfM9/teB4ptOHifQHw\r\nkCe+evSj4cdLwg8NtPxQ7Ry3Ty5JjNH0t45F8Uxh90yfI13g0UEkJK5w7CEq\r\nj7L1o0/Es51Cj5oYDRT+1ptWs9X+A76H0/dwiyWqHwnfNlELMNpym+2dENev\r\nlSiWnhohZB0ZQ4RjEBTzyHt01G5+5NYIeKTuM4AXHmQMZ8Ub6BRxn9gl5fjC\r\nPM6yF+z3vVHO60hIwfzzAVwHMebcPGn+3lTao9GBWsDSHALGFo6+5We0ja0T\r\nCRSke7xYV6I10pGUn6/ph4mF2i/zGLqVOPyxTpYXSqieIMD0dsOrSAsR3pwA\r\nO++LjgD9aRCFGRgAG0ns1nSJOdllB8qM5tw=\r\n=O7FB\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.5.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.56.5","@fluidframework/counter":"^0.56.5","@fluidframework/aqueduct":"^0.56.5","@fluidframework/sequence":"^0.56.5","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.56.5","@fluidframework/datastore-definitions":"^0.56.5"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.5_1645231302670_0.9718486993396109","host":"s3://npm-registry-packages"}},"0.56.6":{"name":"@fluid-experimental/react","version":"0.56.6","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.6","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"f1c43308aef9d73c0ad04b457a83dbf65c327b18","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.6.tgz","fileCount":231,"integrity":"sha512-jLo8pn5N4PnXf5Lyoc494cxZDEjxVU8ZD9cf1pjiLoATLvb7hCwlK4sqrNe++5dfOyB0iZbej70m9AID71B6yQ==","signatures":[{"sig":"MEUCIQDVWEuSDTJSBJ8rlaXG9zllIVGromWj2TGhPOwznkaTDAIgGaYHeK3FNIlDO8aV3LuApzwACBXX5PkyUjAOh94pVqA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiH/mPACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq5XQ//dJugqd8uj5kBtcD9v6KxeZxgNjidwkP1KiKf3Bdx0wuRiOCP\r\nGfSrqa3lLVChsV6ScqB/dDGWtwg8hs43fY/7UI4OAR8NnOadn+Rc/nqWmkP4\r\nNv4GuUa3CYflhXXYFLHIC05c2wqgBhZ1KvTjLtH7BwPmbiJcnObdX8z7WUyx\r\nipiHWU0sCer7ri9H7n2V+wXXyPOiu3SHqP7i6S+pu9rTnVt4vJqJEeDzIhV8\r\nrKHxx5C0sInaIMtOn3hOioF0AunEd2Vv44PlTdctLIYbXSX1vceXuNhFAEUF\r\nUFndgGgc7HGgRIA7a34e7OtrDWJug7uoSOfq0xt4NwdOMawvFwn5/auw8VWC\r\nw+zMa8+uwo+4ErSBQr6HPLaVZJ51yRybhnSWCuY0dtdE0S2i0L1cpR7+G618\r\n0MlzPFCska3pZ5gikSrb3Aq6a06XEbpE4S6A8LWIYyn7w+zjG6U35dz5GLUk\r\nbQCZy24Xqq0irDv3wtvq62tGJvTJHNd2RBjVPzupMUs4ZYUaYNDT75dU0rwU\r\nwOX8ZCYiEIkJfQqa7YW392w1hZ8MisbuUFpJTAwuQLpBBd5Ojj2k1GEJvV3v\r\nGV/6GsDBXe1LBNqjYCfG9s8V1qVJir4CLI02XO63NUCbG0gkJaqZqVlHDAfD\r\nY8P4OY6rEF7dE2DwxVI66D/PRwvMtM4tCGQ=\r\n=ygW4\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.6.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.56.6","@fluidframework/counter":"^0.56.6","@fluidframework/aqueduct":"^0.56.6","@fluidframework/sequence":"^0.56.6","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.56.6","@fluidframework/datastore-definitions":"^0.56.6"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.6_1646262670902_0.413617712875896","host":"s3://npm-registry-packages"}},"0.57.0":{"name":"@fluid-experimental/react","version":"0.57.0","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.57.0","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"9f4b07fe7e4db4e43790d36cdc15b471e8c835f6","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.57.0.tgz","fileCount":231,"integrity":"sha512-Lc5s5lhfsA8al5DF0vqS5/BLi8Mt4fwFLLULjbEMPOEb7ZZxotRBgw5fjYeMUroxyemuMGSJ3IkKV1RyLYnA+g==","signatures":[{"sig":"MEUCIQCLrLe5y/PudqICCpFGJRtASjuycxdsw7WcAkl0yhfP+wIgCQOm+qqiYRNqQG9Rc0xcHwgLRmlaA6C8a09aKHvVC5Y=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiIATDACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpA8g//QWKH8pDeEHExisodNxxxWwTgmk9h2ybLntUC8MkG+6KXtAtN\r\nHGHR+V7UAZzAlKKJgFQKkz0wD6mqCnn37Qy4BmeJ79X+FxkGQUVUeH6Iklkf\r\nOw4jnm92oXGwoTjbB1uFdbVu7FD8lWjkV3a6MjfpZOYVVUDvI5EVIvEZJ84y\r\n00uuJWfFenNdH4m945kG8dCAwK7ZLvE2ZP48syXWu19doSEdv/HQNlHt+spr\r\nz/A4eEES0OSpPjc4UrW/BfFYj9oB/35WCpnzXFOFUOoF6n0gfp+QiNppW2mF\r\nCKELufvbjpmINGFC6nY5lIh2wuMbtJuJanbkhNIoEqBDDUreAyL28lp/HORB\r\nDurcrTNhjvtYOQOuQkf14dB2Zln2yF9Cua77X3CdOtY13I+HP7yJ3xW8tIKh\r\nFJF7+U8Pc6V+CW0CaE6M25WWLImkpgJuyaUPjxuOkjz9va10T0lPvp1kew2X\r\nNvQPrwZ/hf/NxlZIbOwDlGcfZabr9zCs1NTiKEN3zRBYAFAaGFhbh8NzR4d9\r\n4pbJAjXs5xyac1aR5GL+CmTQdO5+Muf5LdkPM6PQE3hSCzkj53R9Ro4gki0V\r\ngCgmw9nu7mbW0KEeuiAbTZaiXfK4JTswbQpTj83efd4DfNoH2YE20xKxNUbi\r\nAy7ZeKVqpYi7m4LYCXtHJlqizubHt0UtA+M=\r\n=OL8w\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.57.0.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.57.0","@fluidframework/counter":"^0.57.0","@fluidframework/aqueduct":"^0.57.0","@fluidframework/sequence":"^0.57.0","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.57.0","@fluidframework/datastore-definitions":"^0.57.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.26.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.57.0_1646265539035_0.458548135680082","host":"s3://npm-registry-packages"}},"0.56.7":{"name":"@fluid-experimental/react","version":"0.56.7","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.7","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"89d0c9b38c8f1613171699a219f9edd37f48c4c4","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.7.tgz","fileCount":231,"integrity":"sha512-gY839/3VPkPXK2tJd2aOtWFOmCyeFAT0m0gub5kQJZEaZW3/UE9CvDPhNnRuqZA3ltIo+Tw5ITk1lrvCZ4gDzA==","signatures":[{"sig":"MEQCICKaWKJ2KG7Nq3vAQU/gzOepWs38WW/omoWCy6qmMasTAiBwnQBaqQ910x4Cdi4+em8O4EMGh0mTrim9lRL4rR08MQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiISHZACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrwNg//d7m5hcSmV2KWk9tzQEDqb7kUbIWhOF3/MFC442DLurnjEswa\r\nt0SIWzsdI/0YJ1llJSG1SS/KUkUWHtK8rppa21deZfQb79BFT33G24DS4Jz5\r\nd8XIanrnpY8XHf6lB0oUZYSPdwt18CvrolttA2A19nibM3preoIq3aMlpWkW\r\nEeuIc3nEb4t/l0+UAa9lxnyz+Y+4c0Q46COwPYAmHtfVthtzlhK8VtkAMcJv\r\nqfu131taSCXkuQMo/KkLtLFx0pC2W9ov6evSzpt+fSxMbm5RWIFZuBnoIblW\r\nJnO1JCBqDuURvV6NOAc9qgkNPcdA/iDDAGJmZeBic42iV7Sa7zADp1yEduBQ\r\no4ccDvlQQS1EadAz4jW5I+qNNy44fRXAOgV5+QjwTxNdbOZZanaN/W/AWjUx\r\nI//H3hNTiJJQZTTqQw8Oyg0g0dWgBctqHS+dwD3/Zq9FqQCJPRZGst7/c3tr\r\ns7G2Y0T5Ja/W3uogg4a454kOk5y88hQTMDuv8VMUECGeod9I/gSEUPxYsEQo\r\nNu9FUT0FfvBxq2Lzu/Km26v1TlEpvTas8sNC9ZnlPxtRAA6MNX+zKysUnTGf\r\n364OhnkYoyp3MGRtBlrpE3hmKbf7+MHmMiJBmlXFs2G8/WTe8mLxBrl4yIaX\r\nULxgttDKTHmNctF/iGRjKsMm92Ndhpr1SRg=\r\n=pEOR\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.7.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.56.7","@fluidframework/counter":"^0.56.7","@fluidframework/aqueduct":"^0.56.7","@fluidframework/sequence":"^0.56.7","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.56.7","@fluidframework/datastore-definitions":"^0.56.7"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.7_1646338521536_0.3742974849309797","host":"s3://npm-registry-packages"}},"0.57.1":{"name":"@fluid-experimental/react","version":"0.57.1","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.57.1","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"ec92f1c95a2b273de1beb231d2aff8044d89337b","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.57.1.tgz","fileCount":231,"integrity":"sha512-rzkWXBIn3Bb58IetXjjpss5jwsC26lEifwwEBz/4aN7KkaFstx+1H/W99mqUG9HUL30Diew2J+xtowANbTucMg==","signatures":[{"sig":"MEUCIDxQsrHJjbsP5P3EZuLM6vv5tq7t7z19eOv42yxgkrjlAiEA7tECqy3z1ib1NF7Tla9M1qJmEpPRFuSLmw7VHuTi8mQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiIW9GACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpssRAAlwvOX9Fl0F+hoqcZPdkY81s5gGj2dImpzNJE8aYwRdwCtmjT\r\ne1LLegH1irqewe9ZCujkyLpPf/Vk3ABlF8/SW+Qp3umPHrPi1pSyDTv7fJt6\r\nDmT4voD21JEq+LmKSozJn2WztCwPK5xXYJzGqJGSYcr/gxbUSWYe8gTBnE06\r\nGQez8vA7iqs4JpbUWpNHe+Qno80SmVtNn1fgixcsTaaoqYqRJx6YKSbwk8pj\r\n5KtDr2PPyJgm0UvOeCAfh9kpNoHkcQusOLQEFYrMebbb1sBMHKTo+bfdCaff\r\n9syvkj66upr/BpdtJkLrdtvXnmXl/czEEFgW5UbscqlrbunGZ0yegE89+HqF\r\nJ34UixiZtvooo3Nb8AIH2nByfjGwEXWmh10E/VVcv1PB41aIS9ctojx1bAhW\r\nHPDA70QCyKtIA3Lv+o9iUgYpDBFwaTLXoVAOI02NWolwB/bJtdFqE0XFQd/d\r\nzrZpepla/nQeU0wsvTgtQo5UWTAZINyvUPBl+qad4WQZQ7wDQLqCsW0q5TyD\r\nO5D+6B3WOcyxZ+W25Igs+VRiDpXTg9PGuLOupGnTpkMrTaSV0BNIaJlWDAWY\r\nqjKtU3zUa9XMGGJpG5dVOPX0aHXheyI440CNTGTrTCmSb2ZAXVUK+cv0zmwP\r\nSMZOyZWQNra4ZQZptRzjd9gnU/x4ZvDlNb4=\r\n=+XRX\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.57.1.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.57.1","@fluidframework/counter":"^0.57.1","@fluidframework/aqueduct":"^0.57.1","@fluidframework/sequence":"^0.57.1","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.57.1","@fluidframework/datastore-definitions":"^0.57.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.26.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.57.1_1646358341940_0.14990946676470052","host":"s3://npm-registry-packages"}},"0.56.8":{"name":"@fluid-experimental/react","version":"0.56.8","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.8","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"d88b237a30504676e7ade8c67f44c833ee8b10d9","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.8.tgz","fileCount":231,"integrity":"sha512-cbgGP1yyP4BI2IR6xQ0SsCPuF3UNTlVrOEF1kdI6Q4pevKRM2Hal0lU5FBAq1YbH/Ah3TSAGaKoZcljgif832w==","signatures":[{"sig":"MEUCIH5MqJM15c+GOXv6YSZjvSkVMMdw/YF9XtM4fQo1FVSuAiEAxVx5btJGe/URRLKlX5jehlX5uVeP/hDmjrE4XPMw3YY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiIl51ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrssQ/8CIsxVfRE+9BKnsgA+2HPkuoMe9o8/ZmQLoCtliUSDF3BVN6O\r\nQouCsLYnYg8rmUfZ4yVna9ujm9KH08O49xO13uv6sckAQ+oZ+RRTbjEtG2w8\r\nKonpELZ5rMQQV6O8RQuaexUGLVWYjX2yRDMUQ/InbUZrjogz9DldcxHP6c8G\r\nTRtkH77xoGkWWQ1EtEQoOdt63l6EmUKBzPhs02iteE9KmJJt/psBcbNGLnvk\r\nH7KfOtlmynIvdUohPhiNLNr4Yq31fy10chiwb00gUKyetRP+3fCrlf+RtKho\r\nyr1cnafSkwRZ/G6CidrPX4qztFwcMBAWIcu/tvRLxPtjXxCFTuHYfjQ7Qv85\r\n2kyNlzYYW0tI/26ydmGmmT0vLIjhaGsGBEQvo7vhyBpf90v/F2aIIRklMoZB\r\nofGa2QSFDm8nisKwzo76IdFgza8hSFWFFUJ2N0C9vnMaM29tqUHWrdsbbkIo\r\nJMof3sGjizzsb+JW2RkM2SWkyLeVFb5lQR88fJSQ3zvkF5+xk1FBUTqv32OQ\r\nOCIvX0LbVTWZEmu84xZdJr/BTa1ksJBGZp+CRuEGuzgKRtY+WHiVapTSMRRk\r\n29So9zHe76YhMVPLsQVUix1b/6wToa1DF03TsAnXVna4dgPACUqdyEgqSF2p\r\nFM7I9s4w7v0l7NCx+dG8xCvVhrZGfccxcDI=\r\n=Q53A\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.8.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.56.8","@fluidframework/counter":"^0.56.8","@fluidframework/aqueduct":"^0.56.8","@fluidframework/sequence":"^0.56.8","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.56.8","@fluidframework/datastore-definitions":"^0.56.8"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.8_1646419573278_0.47098132325298625","host":"s3://npm-registry-packages"}},"0.58.0-55561":{"name":"@fluid-experimental/react","version":"0.58.0-55561","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.58.0-55561","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"6e64c59272d984ef0d67978c5e9ba29d58153339","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.58.0-55561.tgz","fileCount":231,"integrity":"sha512-0Qs3UB6mW9vjdhP/qP/zGF6JU2LapqFElUWLR5MGjdzMFMxrERzANDOlhY6K61l/sHL0PSDCS97ybnudnd0/dQ==","signatures":[{"sig":"MEYCIQDMebrJz4F8FqfhKdJVO/qgOmJFPZfhfn08yK8S5YYf2QIhAN+DnwG+vZyRGQiJirpAWDc4blC528FVa8Zxf4G3KE2n","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689643,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiInEyACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpVoBAAm2ohM9h10Au7P8fpAH/h6VmtzhzVCmzVkl8bMnuP4SrTVHL/\r\n3VYLoh+Vp5d2PFsUYCF7OqGblROTufEnSDPJyRUR29dQaDmpl7oGFxB6CHRb\r\nZ24xbglq6KF5j2joRfToVXYX3MmVlZRMtP9u3Tn9L097otH4bl4iESV3uWqd\r\nfZUtMmHlUbIHrtKzFB9Lufei5PzcmzK8HGLe9PQpn91TbsbTJMKG94LPP8Yc\r\nP5nkfj2fkDiZCbcBd+FRrHP0fmM55CnMvKF2Uv4Q52ka5el7JiYOLcGluaeS\r\nKJj2Y/pVkQz9CDkRC6oyaLYSzNCch1eBUTP9qG9O0ElMgPSiORmusjnrMpzY\r\nAmYMrESNfVd1uT8QnCTi5AYMG5hCBemxtGsLYHkE7zbCESe67QPPBmB2zhqZ\r\njJ1EyZXB9cgtWWI3GXyV9ImcFaYjW4AGSHmevPaQlpW0B9LGwLxFVOmzcm5C\r\nG/3mLeVGatNw9bI0RiXlAiPlOLJKaAC3dn/ZXaZa/iTy2yFW4wB8TfX7GKL6\r\nWcOnpgI7ziUPkaW84hO+1462HUWNhMtyUmZleafjEMLsXme5tDyey9zzZGfT\r\nT3DbH/QqNSjMey6luB2123VaA62+S2TsyJfJuNv8Kq4zUUivrrcmvsyyPrlI\r\niO5Tee2gDA5kfYLHwwyFXOJjdOSMHjuykik=\r\n=7OzW\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.58.0-55561.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.58.0-55561","@fluidframework/counter":"0.58.0-55561","@fluidframework/aqueduct":"0.58.0-55561","@fluidframework/sequence":"0.58.0-55561","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.58.0-55561","@fluidframework/datastore-definitions":"0.58.0-55561"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.26.0-0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.58.0-55561_1646424369963_0.8828886574483401","host":"s3://npm-registry-packages"}},"0.58.0-55983":{"name":"@fluid-experimental/react","version":"0.58.0-55983","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.58.0-55983","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"e67e02e9b12405bb79f2aa4bbf7ef59aeb40fa98","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.58.0-55983.tgz","fileCount":231,"integrity":"sha512-NrVYLU2M0EXXFpqIorvmSGCuPiPj9vPuGTYItrYiY+FTnxHV5KdDYz7O2rM2Vaer26PnzT652NYPRaR1q/GFRQ==","signatures":[{"sig":"MEYCIQDCIZSerkGpV4gjHF5rq9SA0HWVvU+fGx8LiIUsUwUEWAIhAKlork/exYDTl4t+mWl0xrWrH+dyS5PSNGE0E4w0VKUa","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689643,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiIrQeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqoFw//REinHJw4MH2bJFtz4j8+sWqJrFuRcS9ptbhUDPBs2mDQh/NI\r\nRoVYyPRCS0bgSZ8W88tG9OtZzhNw47Pr041urYXBjXHH0EG1S0TSYv6xM9pD\r\ntap4qGZ/219n2HobM6TwS4qOYlvz8om4X3hPPRYJJ33kq0ldvcegn2+D5H0z\r\nJNVtvZtbXtTCeRujM6esSv7iKBYKLVoqN9JXe4kigJMkeHERdH25UB/9rv0N\r\nZYKvQd3GxSkTBMI+DT/jaLcAfxeNp2z3RUSqyldLMQtXGLoTs6P+8Jz6100c\r\nqZcPjlpdRDH0STNt/6PBXwMRJeCJkYJR/MLcZ8cffUzMF4TYsPyGs3IBbDMH\r\nFlqMK3xRv3+tZWA71RI1CLBj+y+kk5VM3xfuNbRJdhqMy45mEW22kYjnCqpc\r\nO9OOPkdbEiNyTSgKlUJDy0iZ0gaeSd/dnBlGszENkOVYJjL1x43X1doyQZsf\r\nKtCyupwv1RDhw1pqzskcxrDOwrxlzlPsm/zt8Iuh6D2GqliNIPzN5HTUKHhA\r\nq3A5qZiOfP/Z5Zf6Vbv/lBAs+TdpPHs45+mgy80xk1tHPIXtIqjuGA+zffLr\r\n7goOpmJBTujGBVElcgGxM/CqaApSyAIvpFTr11RLmghTXNIDMVqWfEYNxeur\r\nNrzXpgrAhtwmO6I/cFjCuHsVwv+ZzBEm+GM=\r\n=1R8D\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.58.0-55983.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.58.0-55983","@fluidframework/counter":"0.58.0-55983","@fluidframework/aqueduct":"0.58.0-55983","@fluidframework/sequence":"0.58.0-55983","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.58.0-55983","@fluidframework/datastore-definitions":"0.58.0-55983"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.26.0-0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.58.0-55983_1646441502369_0.1568975594475046","host":"s3://npm-registry-packages"}},"0.56.9":{"name":"@fluid-experimental/react","version":"0.56.9","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.9","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"548e1ec988f4c6018f30eac5145f7c0df524269c","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.9.tgz","fileCount":231,"integrity":"sha512-6fQUIGjztrLzgsWRe9X3ezBHTqMu65kK/zv9d9lowWfGd2WvGkeItHrUISVNLOyjAxRLiCYrDC4UHTrh6DyVuA==","signatures":[{"sig":"MEUCID1FXkZ/XmdAigHl/1bNMQkYuNuQ5/TNHETrE5nlmi/CAiEAjZbJVXoUHHEilm71d4PVDEyTo6yNQgXcNSDPwgHRufc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiK7jxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrSRg//Y4VLAINv5XWyOiQ92aqcdwKIJsIYm9n0yeyw18RjEbvzDz+8\r\n8Vw3ktqXsOG8RU7zEBKTmGL157VnwRIqG96N7s0wDRLdvGERVxO96KsHSomm\r\n32oA2DhzNwPpw6Q6TtsHr+2hD00nG9eS1Y/KtgxjztjarOVQmNSe4xEihKQ7\r\nkGUq/9pmrI8OO8K5s+0u3NtxwRN3zweqbmx+5EWdEq12+CpNkbyHv2qagcz2\r\nBT8OMReZvN0CN/sDvT9oqANwUQwDdTAU9wmZ+ndrYWrKAV2b6VsXgd2GoFDs\r\nVtrKAcJm1LVe4T/8nr6lYM3F9VtLNvVu30wrIk+uUgj14K5nfacjdjac06cm\r\nb5qAlpjUnJGcXwz/nktJ1mEoOYwzwQ3pfcEpWd+KP5DYdFyC1XpGK/8IooHB\r\niklKKwwwcbMgeyj85TrpHohFyDuPdE3VImzYErENU8acAp1Ozx4DaS1xB6qO\r\nJizXxgVotmZD4daZ7l8myni551v5aVMDExLEd4CQ/clfqbmDvuZ7RDGFlTug\r\nKWSM/QtKPvF0pD4IrX2rrRqR2qzBW3YN3KnHzX/udMfbEdLKmqM6grUHQook\r\nBCVdSMHeP2cXeTIihLWyYBl822P/64YgJtAdYtm/4QTHqaOFZpbBL+qi4mG2\r\nfpmkDR9s1KQI2T+Zbds4D2k5uJJ/COYs/FY=\r\n=qeDN\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.9.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.56.9","@fluidframework/counter":"^0.56.9","@fluidframework/aqueduct":"^0.56.9","@fluidframework/sequence":"^0.56.9","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.56.9","@fluidframework/datastore-definitions":"^0.56.9"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.9_1647032561055_0.9792720830169193","host":"s3://npm-registry-packages"}},"0.57.2":{"name":"@fluid-experimental/react","version":"0.57.2","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.57.2","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"c853c6361d5122e1509335a2dd4e172312e0f6a0","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.57.2.tgz","fileCount":231,"integrity":"sha512-LrQZSGGgzGTgbPMlp9JIlNmg6AemiFAzDwkgb0o87AU8opoYsZEX4BiHjJ2ZRqo6EhPbBtAzTIQ/EfPbbklurQ==","signatures":[{"sig":"MEUCID0i67AQz2Fm/itumHK9IWqb6CV0Nsov6tT5HM118OAbAiEAnPFHWVKXICpMK8SJtBQEIsOxL3bjz8yjuETshL4glbM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiK+FPACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmou6A/8DnlQ7DuoWbDLIF5piFjeDYaQQKggzsaYhelT72NG9J4NKOJw\r\n+lVu+IVm//+FtwIl20MCi1bCB954VcE2QCWnJ/NJ0L9ngrODiKdskDzKDok2\r\n2IF4JWWguN6SrmcvwiL3ydzbLIuCsr1ZdfF91tqgQ/GmSboZTsdTv37ITF4J\r\nKCSYkSuzPzpXImVSjAHdeq2cGt/Zn+nyIQvCA7QQSybOj1v9OooloRyFuaol\r\nBsG9znkXY2K39BiDFQhcyOzMCX5uRo1NJxIL1NMR/BCGabsJj0dqgzKzia/f\r\n5btwrJQvLeEnScX/B6jZ0sz9TW4XAj8HQbRx34TzvxQwbmkXI/Etu2vv2yaY\r\n47Htwwajnl+NfKjbnvxrwAChiX8HyA0yJGL7bDH5axVCngayBs3wfzQUF02d\r\ns7wguly0zh/R44DDgbEKeoypCJ194d/CKApDZygovYinfjqYFZdofNbFrgpx\r\nyBKd+BHbJz7Vb8PF9FykzNr4ZRfunjAze6A+o60Yhk37R4W/G8ouafL1oCE6\r\n1jF9aMKLbSxpglZ+6tiazqt5eCAwqLMU+lxXOMjQwb40xdAj6b0dpUaTPXdK\r\nCYMCjZET7A98/dY2q8zB88opsQoW5XKtwVuzCbss7xu3/FRnvYG1aW/iC5gk\r\nF1cxPreL7rKuJHLfUwttHEnKnITw3rtHEpc=\r\n=61Iz\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.57.2.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.57.2","@fluidframework/counter":"^0.57.2","@fluidframework/aqueduct":"^0.57.2","@fluidframework/sequence":"^0.57.2","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.57.2","@fluidframework/datastore-definitions":"^0.57.2"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.26.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.57.2_1647042895218_0.09732641379329898","host":"s3://npm-registry-packages"}},"0.58.1000":{"name":"@fluid-experimental/react","version":"0.58.1000","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.58.1000","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"79c0419b44fcababd5bfcacde000a95e026d3f22","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.58.1000.tgz","fileCount":231,"integrity":"sha512-B0zacw1HA1bsah9HJLLU+sdSFUylKa+DZH0rzo9gawzLMNa9MfFGgk6TIWf3CE6lR0SCT7Ixgb/wVTLVWpcJzw==","signatures":[{"sig":"MEQCIG5jfyTxO7u6KIqJTEbhq0pDTMgwL4+q9EGz5docsuZFAiBuys6r0Ao2aj1pt3aWXHyTXwmaiT3ILzH8REjPLvRtGg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689626,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiL6TJACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr76w/7BnawdPYY1UfzYkl12/gG+q82AEv3y/12DvSwd4pKPlLQWFWr\r\ny3Hhm4EncXv83t8Ax/xMWWZgY9UEk10QQoTsT5qQIobep9KFP7CdUB0ADnON\r\nmW7eJkPYW19++Iw7EHw6c3dGur1LkS6NhGadgBggiO/phvM9sTcwUsptuOcb\r\nHTu8Grp9izzyg7TQwFWN1SFvEHwhRUbn60Wchd6clV++/8zZ8T2JhIOxLDjm\r\naZjNxG3jn4uXjKrkQZGrQfEktXfGkle+1YF75V7T+/VXSMpXUluR9XtaZz85\r\nMBvOKYCg7+0xRQlEHW+zDkkjMSehKPiW75d1qe1fD2+VEXWWa+J2rnXrP59e\r\ndOAApQaqx7JMUeVPT1BSU8ur+bzNHXXGD7rTJR8c13QSHEBfP2NnsCl4x4oB\r\nBL6rUW7bkteFe9fvNXyKm/FXWIZB00ovDZ/IXpwl2vp6gefZOwD8Z1i7/UKp\r\nJVC7eipbFvImxjP7Z3ck7npuzsHZXKqYOevPD1R+whfQqbFbhjsePJlVM45x\r\nC71eYHBdtDal725NINXN9U5x/fymNhcD21J2KWV1imgOLheMKBH6wUDkgtwt\r\nGuWh1XGa5/q/sPePYkZZgySq+6rqviLBUVRDzNnoAPC4So7h8YOed9ayYeht\r\nOc9v+TV2yViBBmPzaQn4kc5RNpnJNBN4SkY=\r\n=drIP\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.58.1000.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.58.1000","@fluidframework/counter":"^0.58.1000","@fluidframework/aqueduct":"^0.58.1000","@fluidframework/sequence":"^0.58.1000","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.58.1000","@fluidframework/datastore-definitions":"^0.58.1000"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.26.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.58.1000_1647289545768_0.05514460215078665","host":"s3://npm-registry-packages"}},"0.58.1001":{"name":"@fluid-experimental/react","version":"0.58.1001","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.58.1001","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"46745384b1fc04a3c87ff6986dafa8374caab474","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.58.1001.tgz","fileCount":231,"integrity":"sha512-gXxNSwhuCx1+ciE1t30ds5wNmVSKQwMcSYgFKdGL6F3Ds1PkXdVFpa/mYrJJVJXweCpn7QPEJp8eZXL0H4UyAg==","signatures":[{"sig":"MEUCIBOP0Ry6ob1YX63Qr/hQzmPH64yUR4DuR/VmDNfomIe9AiEA408tXi2v79PkqV1qrW9US6/YVjDGCBH9kajJpB9LUOw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689626,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiM+PfACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoAWxAAjspJ5UkVZCFARRhfPJWMYPMuxGL1Cdk0wzDGqPeLv6D/6wz9\r\nESWToCYvJAd4dG9QlkPiw9OvSrYxsMy3fVPrELUOCFyeVBnmy9sS26KGcFFd\r\noPfPrifGr3G4eLf/ykQLD+H9Jcfo4rIgwlJu2OnbSi6s7UDgzh6DxOr8/LQR\r\nShcTWmT5mRxwdi1ybv4GnasUupEGrR4UPLRPaS9o8KJ+sREcb55ah0T1E3Xu\r\ncUiv1444FGsNIYSgDclKN6CnloBTJ/aLtqLDsRjJhWMH2adhKhjK/CU2sSga\r\na2LcGEHVh0lSLHbUatek1U4kkZAyQXvXiLkib1kxELzlHZXtdTajkiTKRWMZ\r\nm0MO3qKlELCKuc1Di03FgvJSSvelQYbLWKn2J7LVX6xSwW9B4IHxFGF889Vh\r\nra47gDwySuiQLOYGyl0uiYOta4sQMsHo1YMDuTIXttxmjhYMjlJzOR9OFERX\r\no/MdNoZEkanNCXi5rGjgFPvTPAZUcFF4HzC/QUi30HsB4FJoGWNEr6ObB5n4\r\niB8ugMkJb9A6NFuGCNXslaw39dbeiypBd7MteFQYEOfzDozdRAjKjxJmFC1a\r\nVpevRYKIV6cmd7MTL7hWpqXlaPgHbgYMGMPmtD8Joq04UeQHQixP7PkAy3dV\r\n9rq5DkFYTrkEAZBhypGMEDyxN8IPHdQ5OyY=\r\n=sFYx\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.58.1001.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.58.1001","@fluidframework/counter":"^0.58.1001","@fluidframework/aqueduct":"^0.58.1001","@fluidframework/sequence":"^0.58.1001","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.58.1001","@fluidframework/datastore-definitions":"^0.58.1001"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.26.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.58.1001_1647567839253_0.16162712324301132","host":"s3://npm-registry-packages"}},"0.58.2000-58133":{"name":"@fluid-experimental/react","version":"0.58.2000-58133","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.58.2000-58133","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"b9fa046fd4591171b4041d81572e8d68776a64de","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.58.2000-58133.tgz","fileCount":231,"integrity":"sha512-wEJZe2fTKY4/zm5YA0aDEoI2H18PViHcEA0i/0raflLOaoo2ivwEu5MClIuDlDtWApJDLR6Fc14PchZ9LRiI9g==","signatures":[{"sig":"MEQCIDFOYGAo9STlRPnzr8q51k2hD7H64dwuSxyDkZkVhUFGAiAJgPL8UiLI59gWy2daoY1KwzkKyCb4rPTiJrxw84oZ2A==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689751,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiNQQBACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoIJxAAlYheGkaabcREJ2IDp9WBB9YtcSl2l1VB8EBlzoCzfRZ1gwSJ\r\n3Z3SncR1uzHHbR9CBQc0PS/BXHykO4P9lFvxmsXxDnSEZOCaQgduM60InTMe\r\nk1gQwofEocd2hhDCO7bsAi0A/tEU6ZTtirpKMNKuopeLQ/GGGq0ZYYCNpMfM\r\ns97HJh/6/WPEqIBrMhXQCqzzpqdsvbbM890o3Nqh8D/8YnCR1Ot6BD8Bg8FP\r\ngD3OTO4ILbm9VsbQCUEJaM08mu7KK5LegMzyL+Y6XVDnUyG3NcWbS85cZsYK\r\nA30w0PXZFm9WOd5wUdRXaL9nwsKYALqLcQNU5g3liG5+Zl5EF1FQyFCkOMQq\r\ny67Dv8VgQdRtyPlvAhFAnXrNMc2HjRZfanNKJrIDB1Y2qlfGV9XAsNr1eVMe\r\nsI11dzmiPETWKDhTBi9/tvFWSuVwP8hitYxRjCbv90pzKOvJToTTHI5Joiq1\r\nHtGadIfFe/3eGjCxiEaGfSkgTQrJCXYhDsoVn8Ijq7jd6s61kb16QGEB9XBM\r\nJuxzq6jsRpQZswC5EQ8jpVpc3QVOOdOujn9JnDnKayv48Qz+ThaHw4cBZiJr\r\nmPaaw1V03RXyLjwuL9Mbkx1OCHSZes9WsP1s2VmJ29y3yIByr4MP0RyLFpid\r\nC9UBgGcSam3LMf7cYDAo11fBQRjvoaWSumQ=\r\n=rkOv\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.58.2000-58133.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.58.2000-58133","@fluidframework/counter":"0.58.2000-58133","@fluidframework/aqueduct":"0.58.2000-58133","@fluidframework/sequence":"0.58.2000-58133","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.58.2000-58133","@fluidframework/datastore-definitions":"0.58.2000-58133"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.27.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.58.2000-58133_1647641600667_0.44480609000623916","host":"s3://npm-registry-packages"}},"0.58.2000":{"name":"@fluid-experimental/react","version":"0.58.2000","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.58.2000","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"e76680bf55d91d1402359c908c23541b79605a58","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.58.2000.tgz","fileCount":231,"integrity":"sha512-IA+L691OrSKVoN3KuwAivFrj6tZ/baXb17hE8/sY8d/rjEGpoZ9O4smsumXKsrzJ1w7GlNwqOqWT3HrT92keew==","signatures":[{"sig":"MEYCIQDxFyURekj4ygVb9f6VvhQdd7C03rsdPs+hZFiISkhV7QIhAN1mjCj6Vj6p2/0h/zeVq/BOxg/EIahdEcMku8at+S4H","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689715,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiO4YxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq71hAAkU4PHlV9LD53DOSb/JX6xAN/wB3DGC2fmyIwwqa3Cag3lYXW\r\n/sIJwM/L1fOSKb/pS2jRD49Gv6Yr4AkScrOPdrAJaIyv0JdTx3IG7nyKNb/D\r\nLcNgjWmI16stJQhCzmSp0nV2FFJ6xuEwpZcxLdoe+dLgu9R77La2n7tbnyF1\r\ndf2lP6VKvPlziJP+2eszYeakN2Vd9AhcI6I+hoQ1F0v5Kc1FUyHpsL2Ac4Xt\r\n+593MufhuxV4wgV1uajewVVmjW4+xriq2CTyXKa8HtfVgioKGLJ3x1W3/jvO\r\nFqD4TTWzqs6/ArOwwjWek1bTTqQN8upbhYpBfQijqDPKfOBhEJ/hMCN6WNRl\r\nVaGj2fZAvnPwLjIGIQKY5ARiYaCYHJhyTU0tDaGJH87KOHBGiO3Zw0jOvJJo\r\ndiJCxs+MnvsRDzjMHVmSA2L44PimC71/F7QhMg89HesIajrW7ME7h1jf7Lwq\r\nlFyxZBU+6/qV5w5tQfGHIQ86PScFMVrumb+YYjr0Bf485myAlxWtvtlf7HH4\r\nUGqg+IQKm1ky6DTSW8VkmzW82QM8Iljs76foXYYlXyuEPCmOZo1lbFejkxBJ\r\nEqgpg8oLwu1SMB5VKT86lfMtr4xpGxYyNC7NPi7wvopDjMs7rwN7gBOMygVr\r\nGBRYmrNolNgbtWqX/CEVFtMIjMhRO+pnvMw=\r\n=/L8m\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.58.2000.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.58.2000","@fluidframework/counter":"^0.58.2000","@fluidframework/aqueduct":"^0.58.2000","@fluidframework/sequence":"^0.58.2000","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.58.2000","@fluidframework/datastore-definitions":"^0.58.2000"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.27.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.58.2000_1648068145688_0.5584283531702692","host":"s3://npm-registry-packages"}},"0.58.2001":{"name":"@fluid-experimental/react","version":"0.58.2001","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.58.2001","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"861e8bf0bdceb92dc0112d88662d8d12404e4d42","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.58.2001.tgz","fileCount":231,"integrity":"sha512-j6vQKoVY5VVuOl2oB5v9LJTvzn8HLn6/Ok4XKZXiBHYHyBRYyRn95ymHWEw8iLap501Aiqx86dX2dHaP4YbZqg==","signatures":[{"sig":"MEQCIAl8dXwV4QGG7vu4vpUm8ESbwuNLJkZaT5QR9APQ9g+ZAiBPYIqmsA+hSMlMD5Wph79Bzka8YI7SEJqPcjp5Hi7rYA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689715,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiPRXBACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmorbA//Q7+fwNcfnFfaSbkThrhHDLEbvhOqaYglnbbikADIMXpa5hB2\r\n6XqA6jllMipgZVi2N4/Th/H/W88OSCjHXWxTOVI3Tt2XUFJY3ZAKQwOi+PIl\r\nJHF7Z9qApNZUDb3P9rYpPfkFSM038VNarteKzJY1CUD3JrayTKTloD3Jp1nQ\r\nDeF2tNdNy5TlzJTwI+ndRA2pNmPZ4TdRNQc6AmxXOS3+Wu72eFYxmbgjMFyi\r\nFGq/WH3PzFmF8Gmd25xxTvvyUJKw2aZzjpxhhHY3lYVoWa065+YE2ZgSgFD+\r\nPFxKT/RryGcs3/u2d9sm/GLoDFCBCq0jUL2fjyeTrAOx+7wGyrJuvJ8SlTdz\r\nwg0fUm68nqQYsrfwqbYmv5RmY/ssheTKsffbK7urdek2cshAeSZ9q/yMm18d\r\nWqNwgA9MLESZPSLCLJu+3vSiAZWD7M24J9r9G8T6ctbRsv82sx6DCveggykG\r\nSpPVq4/RLsmjOiByPYE+hlCPeAN0W6dT6ppxt6SEa4TwSniL9lMXsArl1OEM\r\nHAu1c2E+m5eLM6Qg3BCAB90YQ0UcEhvh2iHb2XxyESnrMznCdFWQjRoKJaZK\r\ngYJm32+FefBhZU7gys5UnEuA3uBzwRtTwqdTqpq1Fiu9IWm5Um35iOG0nMSs\r\nf2HthOXSPVvWQ/r6ss03jZGdnc+Os3meYkk=\r\n=JKqj\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.58.2001.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.58.2001","@fluidframework/counter":"^0.58.2001","@fluidframework/aqueduct":"^0.58.2001","@fluidframework/sequence":"^0.58.2001","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.58.2001","@fluidframework/datastore-definitions":"^0.58.2001"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.27.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.58.2001_1648170433329_0.3983440758479526","host":"s3://npm-registry-packages"}},"0.58.2002":{"name":"@fluid-experimental/react","version":"0.58.2002","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.58.2002","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"3324e82d356f67778d4ae4c3e5eff3d3d6c760dc","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.58.2002.tgz","fileCount":231,"integrity":"sha512-OrWacHfxDhMeAUXLsszJotIaFchmeAixudaRvxf6ZzatV4lnwIJ3S2lY8oJpwivS2SvsCX1XBjz14l3pQSLwkw==","signatures":[{"sig":"MEQCIEyh+AbV1gta0BwnDXp3geSb8cX+H4VU+XDWfkWMW0JmAiAiil5ppEgMNTArt2Gz8YSunXTntUbZ2JXGqsv7dduHVA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689715,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiQgTxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo6cA//ePzsCkYg9zqaztijkDbLRBfyV8xAnapoJzYpZCIwRtgKppKi\r\nUsE2Q9LbGpNL4izPvJYoOw1RkEfM2x7BCfTphIna4NmWa7xP4SzDdV1Fh6Xa\r\nsEcy8Qv0ZPY/1rJaFTsyT1BNsdZK2wWNtWB58KbgavBv/CScbVzwVMYr9yWe\r\no2rTBDPdSNopwiMrZ/J20MJ8mm7Yxe0YZh9iXvNKkn0SEfgkdY7eAYDljqKp\r\nXKBH+CtaVdTykg+VNm2hgKj7VhjrfZfZk+SdJLDh7Gd5bAlc/jtCAIBtRxQC\r\nwA1MErr74IJsqsStcdVEenGvuALGaYU0lb9FGJAG55JND4UWw2TRlwyorROC\r\n+P8xaoO8PkrTg3CaF4wOo/vHgFnaBoxXXymx7qh94o653xoGpPO3OmWuLQAh\r\nhd98CzbazAc8XJsWYhzzB6Hp5/ZDPu/6wm3xkUxQraZ9I2kKlJBZIfqIoBPX\r\nu5vJIq5yU3mPqJ/WaOq6L6UeNtO7PAQnVbVhltwok8swXW9lbklRdKyqgPvn\r\nvSUKUkMIjVHZOGWJxDGzfyIJ2xpWv49ob7V7oazPo34mjgzw/DdxwRv/v9fa\r\nIVR34KaYC9KqhMv1ld3dElC50OG82OxFT7M5uPJkiXfyk1vJtBDJp9N8ERBX\r\nIa9V37N0WxsN1q86OokBmKJwvHjxubCcmb0=\r\n=b8/L\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.58.2002.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.58.2002","@fluidframework/counter":"^0.58.2002","@fluidframework/aqueduct":"^0.58.2002","@fluidframework/sequence":"^0.58.2002","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.58.2002","@fluidframework/datastore-definitions":"^0.58.2002"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.27.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.58.2002_1648493808862_0.17276843330331615","host":"s3://npm-registry-packages"}},"0.56.10":{"name":"@fluid-experimental/react","version":"0.56.10","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.10","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"2d362d7deaa2536ab1ec1c279d656daac098f3ed","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.10.tgz","fileCount":231,"integrity":"sha512-pX/h1jjVVY02R6w1Vmh8lspQB2WdnaNQd20b+ud2G0U1cs8Mfw+5B6chPHH3IGErxU+Q/B3L1gwXVx24BNBPAg==","signatures":[{"sig":"MEQCICfRBeb3oX/cyOiI1oO9PyM6OLj9K50xlfDgtAe6FPXxAiBmi7mFluixqb59R6StVQh1Iz2izJ+EYJZuDrilNn/b6A==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689612,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiQjEuACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpRHw//ZpeSQr3h3LQuG52pWXZgjcr026XTFroQhUQ2mDS1ZfZ1YtUK\r\n/67kcSsrrcBWEq0iLdGyO6r4qNCOJxPQTnsNBBwRIykRJh0nX6aPLHbhS4/q\r\n1ZeeCl7ow7Xmw+JiBqrt8bqZfSstKOpy+bPyVXaMP3KHFoAsGfDX6j21XJAc\r\nUEpH2fGYTv/rRE5iRuhIZmgIvcjPfy3fpR/aCvH70wOmtUcdV9Tbzz+DGjqg\r\n9L1bl9x/AoirW6tFy/9I80NDsEXvCqm6ThrJ+kPCDMdzXAsZy71OgkK7fPXl\r\no9uLX/KGXE3aO74/4hsquV0glqL/blryAHG6N5lZxz6hddroTJmMc+Raxjp1\r\n27GR3hFE+Q4Z/0Dp0Q4J8QYnwH6cH8jiSh1s/kZE30e2thwc4gjppwCYzoXa\r\nLl85DpuRdoToyCjC0CoM+6WKVLHCl8bgWJK/i9DDga3ez43wknvhAtdH55dz\r\nkU57nSiLc+QWsq2IW8kdpft0/UYMgAURItzBSlyhU7o6wnHba8n7PzU4gUMU\r\nihvXLXBNUSIW/SOzUJjt1ssyIa8CuIxbZ/wXynz7uIJWWW9/rPvhJNmH68Q/\r\nlGpNUSj0af19hmfp2NVsfYhgne4O/dUHpGDCikW3wtjK8xY2JQbLPzI80qVK\r\nrFl+s5/+QPBkXtgKSHizJ7zqp1LzQeIYt4k=\r\n=Z5dU\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.10.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.56.10","@fluidframework/counter":"^0.56.10","@fluidframework/aqueduct":"^0.56.10","@fluidframework/sequence":"^0.56.10","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.56.10","@fluidframework/datastore-definitions":"^0.56.10"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.10_1648505134529_0.1451345980144021","host":"s3://npm-registry-packages"}},"0.56.11":{"name":"@fluid-experimental/react","version":"0.56.11","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.56.11","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"dd1292c8228846a338b14e60a9269e97f61753d3","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.56.11.tgz","fileCount":231,"integrity":"sha512-ZDV5mdT4SI0qd48afOri1AVDsSmFVEddc0ME0TFirZO5m8uDR5bu47JHA+CMWKDLx0Rvrco8Ttxlf7bL99l1AA==","signatures":[{"sig":"MEUCIB83B4xI0A8dXOCHlfIYcWyzx5Tm4HI/FFXLEFBiuFcXAiEA0sl2UOIJB/L5Ea6kOSntCk9kg5lDYkAC+h6LvQ31Al4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689612,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiQlFdACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrANw/+PZyQxNOkekZg0Ub+69+Mjm4CvFb4ixJVkisfva/+SkPAbhq5\r\nMBaJe4yKZsLj7iCsjSSNEbHzPE6wBZNJKqXVmmNcy8n21F85prp/jIoY+8w0\r\n0cPGIV9fXMFS+8r1qZAaAJuLKyFMwpzRtV23Hc2Br2tqHgQRhhnMGyOm/O47\r\nYqI421H+ikNR+Q/EMptV7zUZYbhEkUCdbEuKm20hK1tz6/aFgcwGI1TNE5y+\r\nRpDSqhzv+JIdn5FKD+gKk5atDSvvpRBqV2HEe6yl+yPZYKpMS1SHcVgF5mzL\r\nSZR/53feeeqZYcWtX+/+px/lv/4ive+5OM0KsKx6OdFBNxFOXnhRgWcIro45\r\nOxSZ0o4ag5IBrHSsG+oYdXrYQ5uAlMBDG98DRJNAMCItaF845OiZlmv1W21B\r\nkQt+kBMC+ZQnMJwv184MIF63xLrv4Ol3BmfNctN+3IGD14aI3eg8NBlpKoOR\r\nlfNLsWb2S6mOVyBDyA3hNO10iQwoflOJfY1oPjMnTomBRi3gHKyEXq73B+0o\r\nKGmShICV1fRbmviddFO+GpuHxpLujkv9QUNWucCw5aVYiMSKbwQ013T+F/HK\r\ngQKquIExlbxd6WeVqi8aAzxTZWGkfTY6Xm2c1K9/IChifHdvOlXATYTKVk/6\r\n9rRb6Zl/tjPnIajOHWSi5S+8k1ea8eHgXuE=\r\n=Ia4+\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.56.11.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.56.11","@fluidframework/counter":"^0.56.11","@fluidframework/aqueduct":"^0.56.11","@fluidframework/sequence":"^0.56.11","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.56.11","@fluidframework/datastore-definitions":"^0.56.11"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.25.0"},"_npmOperationalInternal":{"tmp":"tmp/react_0.56.11_1648513373506_0.21632493426315813","host":"s3://npm-registry-packages"}},"0.58.3000-61081":{"name":"@fluid-experimental/react","version":"0.58.3000-61081","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.58.3000-61081","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"549f8aa36bb36c74d04627cce20329d7cc92f65e","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.58.3000-61081.tgz","fileCount":231,"integrity":"sha512-VNMnVjy1egcUZPMe9TJ6UbOo2/L/GQc8v7nR7f/clKiDhtg5uOOSsTi07GNL2jSszpKkxvXrFo7KEaKaiEmWvg==","signatures":[{"sig":"MEQCIH4tLRAgilv5Cju1MiyYEGzpxLOJZAp0Dyj9bvzSN8zsAiAwV/p1AA61lgCtXJfoJNI1W6PbfYQWQ1thRJzRJcqp3w==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":689760,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiVNvvACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpc7A/+M9x0ZwhSypG+jCWY9jmNqIFFXUAuzrj2SoH6+b80QjU3glIi\r\nyrGaC6br0TtcyzLwQS58qdHBOAWkJlDwwMIjzzmjcXsfcYzMMLSlImxTEXOa\r\nW10aOSNnuSK8ikAv3kArAp3iOuhMbvfAGGy60whVys8uudMaI9bexQuCFiVE\r\nUxVXKtX7VhQENtX52WQ1PHV4dL+FQTEcqsziX6idaBNIj43yflWoE/FAZ1Vo\r\nf1vjh4ogotiKGnzLvP1d2+dtRtPb1Gf7awQhL8atXlepKGNClf7wFhtg6Uni\r\nTQdxWlJ3riauCje+Ajolf1h+qAkGmFPhwh5EjfU7Ixi8as8C3B87RwcWkKzs\r\nQmznlOEHg6utwJIDl+W0XJWikf8bIlZ1MFh76ZvbmNXsv9vwIyAEPLYMvIS7\r\nhtJ9lCB39DignVBMu62hrjeOpTGlJe+WXoJgn5JVB1MmAoxzSYtDFhRhLUMf\r\n4t4LjwwGGlIQB9NsqOYgp4FWNgqoqeGwJAD8CCeZson/qIQuywEy15NA7gAv\r\n3PedV/WaSbkBCCkXaDuaTv/F3P4HcHq7i0Bm50boeMzqwFFzg9NjQoEq4Jk5\r\nMis+/TBQBWxI6k0//MbNCJFpwfSpRMhddcf0X7qiFsNx1WvUnDwYBaLMrRBc\r\nXTHAsJwsRnha6fLBtVC/vKtvVkuDhdJ+vZE=\r\n=2L/Y\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.58.3000-61081.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.58.3000-61081","@fluidframework/counter":"0.58.3000-61081","@fluidframework/aqueduct":"0.58.3000-61081","@fluidframework/sequence":"0.58.3000-61081","@fluidframework/core-interfaces":"^0.42.0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.58.3000-61081","@fluidframework/datastore-definitions":"0.58.3000-61081"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.27.2000-59622"},"_npmOperationalInternal":{"tmp":"tmp/react_0.58.3000-61081_1649728495087_0.6977132024122923","host":"s3://npm-registry-packages"}},"0.59.2000-61729":{"name":"@fluid-experimental/react","version":"0.59.2000-61729","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.59.2000-61729","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"bb25922b4ec21acf7145ab679dc798b4c08e45b0","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.59.2000-61729.tgz","fileCount":231,"integrity":"sha512-P5BiFtahcmPvaEGHAz/5A3Aii12fzExt7ZMWSbYbOIqLg++R3Y5DYK92T1nbLcl9umT7JyCpKXWhAQCcUjsvRw==","signatures":[{"sig":"MEUCIFtZAfu6W1hNbAQ3X8t0AouyaFqwWCUOR5pPhwJbPx66AiEAv3hFZbxEJ8DWQmAPeNFEqnh4+B9JKEUHZ9mwi+aMsFI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":690044,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWIZrACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoCoA/+L5/1H6D0Wlv+BP2lcuXGu+UHZxS5A+aDX001FHKIum6PYOAV\r\nDtFbLxg4UHYaZhAD8oMy67O7sW/O22vI1euJ9Z3nd+kEdV8DDJ66RWjGByrh\r\nvI0Jk1ozMS9lLs+RzUR13vBRBRSFnvbpZGHMDarhkzpmnJdCU13p/8xG3K4G\r\nZ7cJFj9yBY5ZeYsZ6lzNyeeRCzvZ9KNAFRPAhaGJ6aqIrw9gvYo762eK3Nvn\r\n12WNnkxWq+Wu2VKa/XD7fn16BgjCZvMx1qsSSMOfIVItW+QSwNJ6taDM/jqC\r\nqmrFn33LLc36acJgy3nU/FI7hU1TCjOgoaMhMSWoRlBGhKFItYSLJYr4pD3C\r\nBzIRmvZWTV9Y7UhyVGms7uZUe47EyJI3wNaE2JgoyVPk/xy5V5NNxBTvk1+u\r\nAy7Bt7ihx6B16Rp/zKtjFJCD3ZjVFo5Cv9Fnm5HKfBK4703uaCikO+WtuFa/\r\nMjw9IgYRtg2tuaXhtC1iobidRM/L5AOPqb/CRnhkVZgqCZmkwSgOdsyKw7jW\r\nJXjkoqItab9yAZ46+zaJ1VnexhuDTgS/V3Fwfa6CUp/0/CWQMuCHVJCMDL6T\r\na0mBR9WIfNuYxTPJ5osXosSn0Ol/zbvI9v5zEhGGmW4i45e/KbCXS0Uz3IME\r\nD2t2HO0d4k0JL36fgULllT56yW6oufi8VsI=\r\n=fs7/\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.59.2000-61729.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.59.2000-61729","@fluidframework/counter":"0.59.2000-61729","@fluidframework/aqueduct":"0.59.2000-61729","@fluidframework/sequence":"0.59.2000-61729","@fluidframework/core-interfaces":"^0.43.1000-0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.59.2000-61729","@fluidframework/datastore-definitions":"0.59.2000-61729"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.28.1000-61189"},"_npmOperationalInternal":{"tmp":"tmp/react_0.59.2000-61729_1649968746888_0.14084087965485148","host":"s3://npm-registry-packages"}},"0.59.1000-61898":{"name":"@fluid-experimental/react","version":"0.59.1000-61898","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.59.1000-61898","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"c5d9a43e26cc1f88e2b8e2534f9d0610dd8039a6","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.59.1000-61898.tgz","fileCount":231,"integrity":"sha512-oDwmDv3GFdSRS0wJbHUeqXEppfdGpeWyxepPpq4GwnQVauv8egA6RFbjVIdcymc03QJn6z7ykO7ahpbbU2Ktcg==","signatures":[{"sig":"MEUCIGjrzjF2nFwqe1xmG2Fa7Ir8Hf1Zind1Bcm7z4ZpcWocAiEAtQodAs98UIVQCzsXVh+XXzR/O3V43WOVMzY1zH3Rshs=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":690044,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWfzZACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp2fA//cxEc6ooZXVcCFDh5NM/r9I2FASrM4FHvWMkTuYl1Rwor81Jx\r\nJwzUca0cVlAot5EBXEc1K4F1xUCx/H8Pn9HKQ6oxzccEgJpHhzVzR9ZVkxwE\r\nwgPfzmEt1P5Mo6FP4Ol4u3yTBWv3hYtr/mkTizLaCVGWOPOIV76ukv35LYMm\r\npnJBpaxNJfrQJjTxtkBH1WwzmQ1AXwpvVqam6+Ke0SDJ86J9YmBdO0pz0DHM\r\nja5egoEbWWrAh+2Msp7aiCkMYdqfMZhaqGeKqwMFZspwTXIlt9kA95hPGkl+\r\nEJ8Fg92whV4bv2MyY17pILrLBL0pS6Gt8bbcZS9HcklCUoawKf92sab/RBDu\r\np2k9hiS5izBc82MegqtsuR01kE2tHRtzWTTQYgRbB3SvSg806x6waKj6+dGt\r\nd7iM6oq5sMqcrnqZgHrAPpR1R8KHDGgjy1asHkgL6wCIqa5MTPZ85Beimm1n\r\ncbAk7l5wvJWtZaGcpa3ahnPnXvSsqYe3MvXRBbJjw4uW77d6ZRYBN/9ZgLf+\r\nZd7pzza1O05ry92KHdSQaFf4D/cbMuqR7lVYjf9b27IO/L2kYmEPjpTmRfd/\r\nhBgVoDLgHzXgiqItYgPJ+cHWrneGj9vPdGIOoVWD8FgnUsj1NgIOO/RyhxMp\r\nWpL4K5VDpXfuK474JUN6hsWadgIGnRv0TtE=\r\n=Nabo\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.59.1000-61898.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.59.1000-61898","@fluidframework/counter":"0.59.1000-61898","@fluidframework/aqueduct":"0.59.1000-61898","@fluidframework/sequence":"0.59.1000-61898","@fluidframework/core-interfaces":"^0.43.1000-0","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.59.1000-61898","@fluidframework/datastore-definitions":"0.59.1000-61898"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.28.1000-61189"},"_npmOperationalInternal":{"tmp":"tmp/react_0.59.1000-61898_1650064600797_0.4634641452253281","host":"s3://npm-registry-packages"}},"0.59.1000":{"name":"@fluid-experimental/react","version":"0.59.1000","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.59.1000","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"8898e01d1d75d114d62971ca0ff342346105b0e7","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.59.1000.tgz","fileCount":231,"integrity":"sha512-tDbTvMRKNDfucLiKm9LYoohVV3c66xIi2VTlAYC8KswSTfvP7v0InZGs1313LDvpzEXg54swx3ibXLIzt9BJFQ==","signatures":[{"sig":"MEYCIQCpxGO/iwG8URhhVwr9ligmUGlHaDqwU5iB+dWcUyjI6wIhAJ0Nwp1dzGJKwrb3dR/7ldIyPjHoYECthpUIxC6k4AWI","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":690000,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWnGdACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq+Cg//cNk0vEYX9ms+WDH4DyparRFw7qa7O93G4aJldz0pr16YliYs\r\nKN/qYmpgmnd6lnPQ1aOccsKoFmkYF7++ZvsSNzpGP1LBncU78sMK7Lb69otm\r\nmlT1md3rvCLJudjKFyyj9/dVwLpuYyEcXXtPzZb9Nk7/5RkeK7fCKwJLg/oi\r\nEt6BTE9OQRifZTrZxZ5kwsivRIkCZlqionBHe+7/n9prZn07I02cGNHd/a1V\r\nKlXCjiuaOhNi4FUxP/fJl6VjoU/cYrR+AVoGb0SI92aNDHRmIKzikAWO6g09\r\nPZwa0MgYmInA0CQJDqdVq3XsdvWa8xTcU141wSGnTyxxJNTaWar20oKOXT44\r\n00sCA1FhjBj/IthyWDcXi4ssd9EJ2RZoF2/5d1DXfD0owLM2aeeSMjD0eom7\r\nq5TIgEgnGJl/gT1fuC0N6Gxzeihzl5iz03Oxgyv5CHsX06VK3k73mjiiDFaj\r\nosQ05LX8MlgMxAasXL/5mGJO1eyaJLqq4v4+67ZwSspKXT/Zvpg0pFm/mEEy\r\nauzJAmDUqfCU9r0jByivuCA1TA6PoOBuNSSg98Lj/1VAdVMGRgpBmREJCQDu\r\naL2gW8UjuQ+ly1TQx/X2l8RZauZImsaKBDAma3kKQf67ou6bRT0r0qBsIfjG\r\nI+9TyqrFQw/S6WUL9Ss1qkvEV9YgKcfREUw=\r\n=5lFo\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.59.1000.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.59.1000","@fluidframework/counter":"^0.59.1000","@fluidframework/aqueduct":"^0.59.1000","@fluidframework/sequence":"^0.59.1000","@fluidframework/core-interfaces":"^0.43.1000","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.59.1000","@fluidframework/datastore-definitions":"^0.59.1000"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.28.1000"},"_npmOperationalInternal":{"tmp":"tmp/react_0.59.1000_1650094493607_0.08354945330382124","host":"s3://npm-registry-packages"}},"0.59.1001-62246":{"name":"@fluid-experimental/react","version":"0.59.1001-62246","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.59.1001-62246","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"c8f8a1dc6215beb26c4b1de20ca5c02df8f9d9cd","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.59.1001-62246.tgz","fileCount":231,"integrity":"sha512-ZIFYJy6LanXoU60lusUPU5Z/UoQtQSoPjGD8bJj5eFRGaegYTIAwgp+INIg6a9Rf3gJ5haVobyXY820fQXuHxQ==","signatures":[{"sig":"MEUCIFbv5eNlv64el/TMes74b9HR9+6AJcvJbuyy+epWsAFXAiEAsgsc2/Ml1MMOHBPkjBVHwmnHxn/yM+/mMcc7E6+3Ubs=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":690036,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXbd7ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrzQA//cxRdiH3w8jqmHM6jC6F+cgItmVVyQAiJIJ5IHpgOYOsndIEs\r\nGYOGOLBehh6e3ab/HMU5//0uQ4/X54CZGFrmX+3mdQVhdABGQaOPMUv/AOLL\r\nMbg/Gz+Fuwy8NDPvTgrsUUVY3TnpSqmO5HxoHXQFyg6XJvYp+bMKXZI4s+nW\r\nRbHqyx1kWnfQ9vO0b2IXPfTDpM+wLeKYejCOK+Ob81ib9v1wMYwVVK+HoSQe\r\nEcRfSGR/7O7wYqL/rdiAiztUUxmzrf/4nJyQwlIZjKeSefNMtEJk3UxCLsDc\r\nkktjY9QdsJNf9FOSJCSxJPbMnnsQa0nTJx7EftxWGD30Gvkw57Gl0DcxpiLh\r\n7VLlG9zE5kq9PjCU0JaJMWtZohjxtsEukT2EmrPCxkc+/ImJpz96j0lfTG2R\r\nzP7/K1Ug1xUfJ+6SMgQdivrQUwSsayrhyPiRIVuC4a0EqOVE4CBtzxm9SB/I\r\nDue8LSggZ3+cUjB5ZbaNmFkOhnmEEMSNl/1h3EErHsC0AAA4+iiKrFi/Et3W\r\n7rXMacwX4o1/6BFBGHPQDCteYRKb2mY8krmFUtovPFGxPkG6+1lMbDdQiZE6\r\n2f5uInn6IWl2Pqt/Wq0tmyg+p+uxfhAYqsRq7sbRlIrV/stH/sKutRCb+hWy\r\neh2BcSAWTWbbl9/4AHAtLXGLtBDBf7Jo9bM=\r\n=81qw\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.59.1001-62246.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.59.1001-62246","@fluidframework/counter":"0.59.1001-62246","@fluidframework/aqueduct":"0.59.1001-62246","@fluidframework/sequence":"0.59.1001-62246","@fluidframework/core-interfaces":"^0.43.1000","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.59.1001-62246","@fluidframework/datastore-definitions":"0.59.1001-62246"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.28.1000"},"_npmOperationalInternal":{"tmp":"tmp/react_0.59.1001-62246_1650308987527_0.2619402951334018","host":"s3://npm-registry-packages"}},"0.59.2000-63294":{"name":"@fluid-experimental/react","version":"0.59.2000-63294","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.59.2000-63294","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"76acb6836e864a43eca7af1e488edcf5e37100b3","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.59.2000-63294.tgz","fileCount":231,"integrity":"sha512-dR6TkwjKq4ea+6XtKvNPG4e7BkZBuEddYljI4Jw+HV9UuxOMRNsKRTKaSB14XBCC75sekQpAtPtvRNdQXwVsoQ==","signatures":[{"sig":"MEYCIQDaGmPWlobtxiCBgKLxuoQuDGW07cRHXWIXKlMSwNuLrQIhAPh2V3lREK+9ov1gwfpHLDiOxHCe+goo6ePErgKXeFv7","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":690149,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYxnPACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmoiwg/9GJeh4t1AKPqpADyXsbWwW4kFs+D0bJeLwRrCnv/25e1mb3j4\r\n74PmjIjXWlaOqiR6vBCeIyVwR7KpX00Sc6KsfW7OwiriX+Rx8cP6toGLtXK+\r\n2gIFSbdrTqoeq5okUusWhI70qb1R39SsKb9wda7mgi1CPKTNJT45srKdx5Fm\r\naCduqC7v4gphMMss0fl6r71I1OVJf9R87kUxcrOaT0TDCMEfWpRPx4fw9nOe\r\ncz67pZ6A9kxe1MqGH9tMm5Ne0qVeMlI60bj8+8YphaoHgL6YvteRrmWs6+hk\r\nq47oA3+/Xh4WJgebqKMZVBsY/sRBGpwJ4monETFeTDmgdrCqfNSRzfd7DigA\r\n73nkrilyz385rj3MDKxKW2hvzTI93pjZXhsac6zgzem85QTSqw8Z7iLkP3Sq\r\nYWT5v8hiaVo1f1HMp+y4BKAg2wfsMX9lKfDiCpWbyOiZyorYrf2PpyhGcc/u\r\nzXy77woAro9ITG5dtowVR9gUilAdGCa8NtEh7zw61RRfbnMsryVxk07eiuX1\r\nWI4uqeWapHiYz1nGIdhdsf/0QXIelmjlTn0NNvsc6Y9INk+U5HIzYraUOxXc\r\nR4JPvJ+GzPPQeTP8gz16zYjdXwyUOduyWcNVWjnJ7zG1cJwF4DixasG+Ht3f\r\n2KRwj1LE10/MnU8yLUUHID/dWw/2sdRC/LM=\r\n=4Psh\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.59.2000-63294.tgz","types":"dist/index.d.ts","module":"lib/index.js","readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"0.59.2000-63294","@fluidframework/counter":"0.59.2000-63294","@fluidframework/aqueduct":"0.59.2000-63294","@fluidframework/sequence":"0.59.2000-63294","@fluidframework/core-interfaces":"^0.43.1000","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"0.59.2000-63294","@fluidframework/datastore-definitions":"0.59.2000-63294"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-jest":"~26.1.3","eslint-plugin-mocha":"~10.0.3","eslint-plugin-react":"~7.28.0","eslint-plugin-tsdoc":"~0.2.14","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-promise":"~6.0.0","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.22.2","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.28.1000"},"_npmOperationalInternal":{"tmp":"tmp/react_0.59.2000-63294_1650661839344_0.9003184871463246","host":"s3://npm-registry-packages"}},"0.59.2000":{"name":"@fluid-experimental/react","version":"0.59.2000","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.59.2000","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"09d445f7038f9748c11eb7394d0fdedfded92317","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.59.2000.tgz","fileCount":231,"integrity":"sha512-zK8w3ZN+gNIDBIGopRtxON4vH8GLba6ZyXL7fXglwFMp3GCdqUlk2NMLnslOAVQ6deKyDfMbYJPqmUO/QD3d9g==","signatures":[{"sig":"MEUCIQDVqM5JX9zJCZP9+nRBUc/xSpSwSdfS3UHTK9FD/rGjCgIgOE6Lu8gAiwuTG12+7MQKuNM2CXaLZRpgh5UgHldOap0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":690113,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiabblACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqqGg/+LcZbEa9cdWDuGwKCC0sNPOX5pOcMUeBBnEvBymK75xEZT8hD\r\ngT6fHUp4Z6mzSOp2R6hMfOAzfEHVH0STqmOTmJRWV8hh/xBg1NnTSorc/2O2\r\n0jixQ1fO359SUNoqZr3Vp0R9t7lqDFlGsSh5cfv6v1XGlcfdZ8/NnjgsRbPV\r\nGR054ATuQSQoJgLXo03C9lYE9JFnP3+zfjcrS8KiZp0ir6AtRAIZAQpkTdQo\r\n89pa7xMUyVJbUeK6GWIUS/UsCTNkosNpFvJsCaJPw52+Z8DqSVjE5rkXAmrD\r\nsVoTwkKFtL4VpmDHwIcJqvEYuAlS9b9UdnwjTr3/kcCoG69QhS2cF8233Jod\r\nqu7jDQvSlD9GujGIJ6FjVSLfG97r/LofyQwqlXJrXfjmhImWy0CFnCMYQcB4\r\ndKkYtG46GBVPHA8RCO6JQaE0sEq1zGqOAjOb6NqNxjxZFQaYH7wcP/8PxM3c\r\nTp4Yv6+7LuKIF8Kp8H+BCMIfeFx6XWLAAwJUzCPlhQKmKNTBtTvGnN3wq6KJ\r\n1NUUKwnvlJN0ZMUnbH1y3w5vRnGouqFgu3vOv/E3l9iQMrooB5YAZX2mvpAp\r\njgOCiYMyUNFN6OmHZSO3cspIXvBEwxtIRKq1gcLd7LqHiyYbGxg0GHj26qPQ\r\nxGdirlmykqNXpM8Lx6dYwIMzdHcsawIFNco=\r\n=e1Qd\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.59.2000.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.59.2000","@fluidframework/counter":"^0.59.2000","@fluidframework/aqueduct":"^0.59.2000","@fluidframework/sequence":"^0.59.2000","@fluidframework/core-interfaces":"^0.43.1000","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.59.2000","@fluidframework/datastore-definitions":"^0.59.2000"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-jest":"~26.1.3","eslint-plugin-mocha":"~10.0.3","eslint-plugin-react":"~7.28.0","eslint-plugin-tsdoc":"~0.2.14","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-promise":"~6.0.0","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.22.2","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.28.1000"},"_npmOperationalInternal":{"tmp":"tmp/react_0.59.2000_1651095269403_0.8977397673244751","host":"s3://npm-registry-packages"}},"0.59.2001":{"name":"@fluid-experimental/react","version":"0.59.2001","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.59.2001","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"10511e78ea13a342127e4595d150173b4af169ff","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.59.2001.tgz","fileCount":231,"integrity":"sha512-ZJEmyr4/cy5PDQzLTsrsAhkQJGET4QYMBjAb7eYb90Y9wc6fACewA7WiWOjh65/Koi3W+zAmCKqQmc9dnucnjQ==","signatures":[{"sig":"MEUCIGAMjxM3Q840bVPXoDqKD3KB35u484pa3bHzti4FQDB+AiEAznyufAqlskRsJ02Wwxf8d0faqosNVJf8xgsCFjaTD+Y=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":690113,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJicaCzACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpcJQ/9EhV+qbtOBLjEBp4ecX/b8aJ/U7egyNHVxCZM5rWon4NAYlCG\r\nlgoAPag63yjDSERwkPkPlvzyen0qMPwhhZAiR762ogbVaqD20LlL5lIkgG8I\r\nAJaqLJqPfluJaDo1vPoiaOokvf6C9sN7OI6rEX1qzx2LC7xq4VCpPuwG/3Qc\r\nJYujPSnWr6XuC+Ga98/uKNmhsFVP7/qjOBd12hbEDyVaZFrETcZmDiCr+1aJ\r\nWVmAr3HBZc5RptjnzQyWv7PH5f7CnEcC/BspKehxmXKyhbSw03Qdvn3Sb0jU\r\nMnVKrbeKbs2aSg8ig6BES5O2gMkzxlLxOzvfUj96ceZEunhyx6IyvAft6mXf\r\nwrNgDX3fpAnMzj1EcCcT0NdcEd+YcWTht4Y63gwQTuYTqCnvRPsvDDHFZVRk\r\nNwFn5fwLuWgLuVAAldhkle/WYNXHJQKmTczo1yJEj3me5ugnVl2y+wi71paW\r\nRGwvdSiB/CJoN3+E4Ln6hoWWZFBjZ1x48NE3NoVdCVPAtAufxisUr0q5lK9c\r\nuqTexP+7jsEpOdrqT+2yZnhAUrBrombeBo3Pr/hfLc+d98d2Eg9xob1rXJxK\r\n4OR639JxpX36y6Zvp1SbIj6RnSHmGBnFoErp9ouh0M43OgT4FqyfGhTK/kkC\r\ndMq7k7YR/T1j5/00KDOmVrEAllFNY9DkWQg=\r\n=zagp\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.59.2001.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.16","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.1","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.59.2001","@fluidframework/counter":"^0.59.2001","@fluidframework/aqueduct":"^0.59.2001","@fluidframework/sequence":"^0.59.2001","@fluidframework/core-interfaces":"^0.43.1000","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.59.2001","@fluidframework/datastore-definitions":"^0.59.2001"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-jest":"~26.1.3","eslint-plugin-mocha":"~10.0.3","eslint-plugin-react":"~7.28.0","eslint-plugin-tsdoc":"~0.2.14","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-promise":"~6.0.0","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.22.2","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.28.1000"},"_npmOperationalInternal":{"tmp":"tmp/react_0.59.2001_1651613875477_0.717397618514958","host":"s3://npm-registry-packages"}},"0.59.1001":{"name":"@fluid-experimental/react","version":"0.59.1001","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.59.1001","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"b2826216a1a2af140f8b1a01b0a6fb313f33994d","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.59.1001.tgz","fileCount":231,"integrity":"sha512-CdmXgbcqT7qrFlFy671orhvTUIWH12H2AYntEsrKpStCAyjppXZPWdpAEHp9hXRVimj9ejIKxs6rKLdfx6VNug==","signatures":[{"sig":"MEYCIQDiFocMj8k0DTz/FviVTLxmdLaTi+oQ1DBNUBxD7m14XgIhAMFflK30Zjw/xE5xgSnTx7JV040OZYwXXUsvjLxw15sc","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":690000,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJihqvAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqsHg//SU2lbML5BGvRpnhwq/r1r4nxOH7pmA1afyKIddJqSTkcQ6Bh\r\nBZOKPnFoFxxhmZBgsIjRybpeejOzO+O7HqyntaX3SpPpMyJe9MCJKUF14Ael\r\nQxWp02Us7gW5tBoJlCgoXvqZ3vCzNggsV1QYDshXg/FuRTKP3P5d1jBM5LUc\r\nPaktQJQmQ/9ez7sjURhVBRiXOhapIIP3YwbkBaq3/bhjHekMBLdgvohRXNK4\r\nJ453sGPl2HSb8cJ3uvHJeaqoa1KC1xmhBntbX6LUjmFkRQGBB6egqAQJILo0\r\n5FuYj6GmMRPBVUuC/+yFGBunxqW/tXCTehrznVupLw7jw28Bkcywl9g7Afdj\r\nPhYS8TqgcRR/45bP7a0HV41kM6n6I5aY8Bsltfq1IWJFvYsOfR+sHtnjG6JU\r\nbJCnPcvkcTg2pF+sTZItnr7wi4HjGBvNgCaY4+AYtvnyvpw20HjxZ66SjAbE\r\nXSIAoKmU+iI2h04833NgV2z5m6LUs+P/a+P7H0c27eiC9uV2BNzUbkhSIGiv\r\nQNKuiMrfUvaCacz2EN1ukoU2s5JbuqtPx3eVn5UzPMZldBq0HXB4apeAXkSC\r\ncXNpFIZWnanWnOvT3xL5rA4lGaDEXqRz0Ac05ePrcwClf9XOkcX77c6avDFP\r\n0Up5IUePSazNe4xEjAAzQARNNRpBrqjaALY=\r\n=3kDn\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.59.1001.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.17","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.59.1001","@fluidframework/counter":"^0.59.1001","@fluidframework/aqueduct":"^0.59.1001","@fluidframework/sequence":"^0.59.1001","@fluidframework/core-interfaces":"^0.43.1000","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.59.1001","@fluidframework/datastore-definitions":"^0.59.1001"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-react":"~7.28.0","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-no-null":"~1.0.2","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.16.1","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.28.1000"},"_npmOperationalInternal":{"tmp":"tmp/react_0.59.1001_1652992960615_0.34296294599096977","host":"s3://npm-registry-packages"}},"0.59.2002":{"name":"@fluid-experimental/react","version":"0.59.2002","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.59.2002","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"a1c6102c94c6a9e4b34e6e1a0e9c6372e93afd8d","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.59.2002.tgz","fileCount":231,"integrity":"sha512-hFpnzBHq9bp0CURGdh4PV/rBsyukT+CN0R1WOnQ4/veF0z8ZBe+SBW/9DnPqMA2EWyB6sCGlbV0EZ+1xB+B/BA==","signatures":[{"sig":"MEYCIQDJB/KKlMmcH+TPnFGL+U1vohoSdN7/dfSdr/EQn6sCqAIhAPCee5zLlRmLCxyff6v7lT8ATm4SBeRlCQ3IPzYV/ZTM","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":690113,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJihsjYACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqPqBAAmR1vZdWnSszLMOuO0mE0u6APd09+XMGLPCGmz6PndCHfoNxJ\r\nCSdOEjRtLTse4samwiFVUUx4eTUNMgaeazt0soB5KiIKg8ZAuJ5l4oTc9GxH\r\n0hnscg9ITgLS48x597yn4XvXBChZE9qJlwkWhXO9TY/rX2vQGqrL0mnpXRUb\r\nIFkV2wUiXNZsykmB9TC/Oz2myrxB27kP8FSMlmH12GDKIGeDdaRX6Xk0X8zl\r\ntujULHzXUSJNiUJNSz2dGtjFj0GgFTArmw7+6X5/xHXXuJqZ/OcItN5V4COm\r\nGs39VJHpqYun1cHj43v2ZZEB9GG+a5NjxzOD2k1h6RO2bumEb2KWK7r3KHpl\r\nTwbZc9hTe3Es1cHch74x9zapEPaWMP6+J2va8y+dqp6AdD/Lg2E08TcnteF1\r\nJaxxzgsErIb+d1umA5nuD7Z7+7HaDq1DJdbH4kNmBIat6sPpCBFdfT9GubiN\r\nUWnis5nmGa9PrzbehyKOyhuKGqyAmveUHUOUbi444WaeKcnXUf9YRaoxzqLv\r\nxrpMoCZzhelAOz830IjtfOnwuucRpLHUBMEufz224HoDXGsTGHeA4aH2u4ej\r\nj6ToEGwnij9GBElY0KRQwKI2geXj/1islhLVstwmCkO2TxdGnwDsfd4eWCEk\r\niLYGx7UFWDl0WyUdgyxOKyIX5zxgTqU5OZc=\r\n=D5/i\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.59.2002.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.17","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.59.2002","@fluidframework/counter":"^0.59.2002","@fluidframework/aqueduct":"^0.59.2002","@fluidframework/sequence":"^0.59.2002","@fluidframework/core-interfaces":"^0.43.1000","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.59.2002","@fluidframework/datastore-definitions":"^0.59.2002"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-jest":"~26.1.3","eslint-plugin-mocha":"~10.0.3","eslint-plugin-react":"~7.28.0","eslint-plugin-tsdoc":"~0.2.14","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-promise":"~6.0.0","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.22.2","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.28.1000"},"_npmOperationalInternal":{"tmp":"tmp/react_0.59.2002_1653000408330_0.6934223194821081","host":"s3://npm-registry-packages"}},"0.59.2003":{"name":"@fluid-experimental/react","version":"0.59.2003","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.59.2003","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"a18abb87cb402fc0e71bc10ee8bdcb42cae2de54","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.59.2003.tgz","fileCount":231,"integrity":"sha512-ta4es4qq/DqhFPsTWcGJBlMGcBBAbYvC1/T1ukGcxcLr3hCV/c/BnobTigxb+/+ZGFOQ2F7Rhiw/EOcyCIlVnw==","signatures":[{"sig":"MEQCIFTNWCm6HIU3qkD0WSniOV3APwqH6F+6LQ3ilJnE+eAXAiAbp7uxTpvX8uBRqkdvrn94lkyc/tsdZaZiOdFMD1fFeg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":690113,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJijpNOACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp70A//ZocnAfCtO27CCK9seT5x34QpJAKu9UMXLM6w38EDjXbO+Gtg\r\nf0sOQXFryUIGqf8T1QbyBr3Huc5MSTorKSzxOVIxpxkpa3RyTGpB/lZs4X+p\r\nVg8p9NGDuV60ZYZYiAJkyg58uRbO4okjZzS9u1NiIBgO2I7Kpgq6HzUX//Xf\r\n4Q5yxMvZHw3xIGVT2OaYYbuvc95T39Yu/lFaNXrXImIxMGRGcvEexrXQICuE\r\n+HLDfRETX+XMX0fBsMvsh+xCy83imRkWGl+4YqrSFHd9KB5cgGt943jyK0ki\r\nKrrZ5+G1qVZI2tkYc7QuYMw+0CRBIDZ1urnosbGO6SSdLuqRx6JgS+wEV5vp\r\n4zRmrH3bDhsJcKFgwwGqNmPl6wTEEgE7L9YAEGuCiK+qzeq0x/pdR3Ctd9f/\r\nLsYzPw5hkyU3NSHnwFcNDHZFx4ziSpMDOPXsQQyx82OyA4q9KnoM5omY6YRC\r\nUXRFOG+Se12IpbwkzW3VbkoK6+JT5FjyIb2wzl7+VPKvbKnMBUAinhSx9Sv0\r\n3ZmS0+zERbJ8INk+vwR7YKGVT9g163E7ASXgEMISyYxzopaO/yt5ba2v+deW\r\nNq8LdV7e4/5JsJuKruc/WR7VzQLdN0OlMmIFNSMVHsObSlStxTMDNujvKnNO\r\n2JVTVsblkOBZ1WPIDNpsM+JXOQ/HAxzoCOM=\r\n=D/JK\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.59.2003.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.17","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.19.3","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.59.2003","@fluidframework/counter":"^0.59.2003","@fluidframework/aqueduct":"^0.59.2003","@fluidframework/sequence":"^0.59.2003","@fluidframework/core-interfaces":"^0.43.1000","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.59.2003","@fluidframework/datastore-definitions":"^0.59.2003"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-jest":"~26.1.3","eslint-plugin-mocha":"~10.0.3","eslint-plugin-react":"~7.28.0","eslint-plugin-tsdoc":"~0.2.14","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-promise":"~6.0.0","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.22.2","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.28.1000"},"_npmOperationalInternal":{"tmp":"tmp/react_0.59.2003_1653510989960_0.16188912584338477","host":"s3://npm-registry-packages"}},"0.59.2004":{"name":"@fluid-experimental/react","version":"0.59.2004","author":{"name":"Microsoft and contributors"},"license":"MIT","_id":"@fluid-experimental/react@0.59.2004","maintainers":[{"name":"curtisman","email":"curtism@microsoft.com"}],"homepage":"https://fluidframework.com","bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"dist":{"shasum":"b5c4cbe372de6c328baf41af36dfe7816a25ac9d","tarball":"https://registry.npmjs.org/@fluid-experimental/react/-/react-0.59.2004.tgz","fileCount":231,"integrity":"sha512-ooJfLRn4j/6WGZ6pParVKgKz50AJKC6WJ/IS++SfVaZe5fILxcifz3BZcunva7Q9+g01KGsm7Tm9m/wBVUuhfQ==","signatures":[{"sig":"MEYCIQDDaG8N+gdAKvMU8AT5pYkKyokSANVlYlJFhrsop3DJkQIhAMcYY2k8m64svq1nDJLCh+z+OSKBkR4gYnZdi1zN3+7p","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":690113,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjI6WoACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrJJQ//bEj4Cza0giFUgG78Qp4HZ1Qgsw2QqDurDnWVn4dLNquP4Znm\r\nCXlOqvhoaDxpXIseoj7bvHtyLAiHtcriV2JbSrPLDhfCvOeHJZiUwdu5/uYe\r\nW/kxEIo16FkGfC4jczp0kML9G8J/XIZvtcZOuWVC7pMSvnlgkdrcP5shul93\r\ns+c3WAxTTW47zjPEvv4tLtPMy0VzKLrIyv1HcAWhGCFbzacwXaoLu+4om+dA\r\nLq845aonOwGeBpKIas/0PGlB5pQzWXYc8izU4tS9meLsrq+unUhx7n/aOOVZ\r\nI4YZQxTYHoDPU1SDgJwCqtAR2k78ZBHMdOyse7Oe1a+JaQDji3VOBgQmQNtu\r\nRfHZBhm6AgPiKsk9+/UNcIEEHrD14JS6ye1/934y48xw5NdlXipf1jhh+eCO\r\nCt/M/0I5jwkgGubYSOLRoTjQmT/2kIVaIDJD82W6JwDmrRcA9hMaDjn858mZ\r\nkNDEJHwWX6rDZhAXFOKJ1WgsQ+tWfx2mk2GpR+Cf2lH55lRYSnYHetqqy37b\r\ngYZyVP6eZu35Mie3uot0tdDb0vbzIeY8vkB4PiN0XdOkKuFLPcSfAiW5Q2Mf\r\nKxvs7A3+YVkzNEuVSHw17GhM6tQbLG3Vcq+igA+X1fapqZoPiwIgPYpin0Pv\r\nyCXJ6rE/QitwfjEP7PQRS1eH7U975Rdl3O8=\r\n=ql3d\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","_from":"file:fluid-experimental-react-0.59.2004.tgz","types":"dist/index.d.ts","module":"lib/index.js","scripts":{"tsc":"tsc","lint":"npm run eslint","build":"concurrently npm:build:compile npm:lint && npm run build:docs","clean":"rimraf dist lib *.tsbuildinfo *.build.log","tsfmt":"tsfmt --verify","eslint":"eslint --format stylish src","lint:fix":"npm run eslint:fix","tsfmt:fix":"tsfmt --replace","build:docs":"api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/","build:full":"npm run build","eslint:fix":"eslint --format stylish src --fix --fix-type problem,suggestion,layout","build:esnext":"tsc --project ./tsconfig.esnext.json","build:compile":"concurrently npm:tsc npm:build:esnext","ci:build:docs":"api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/","build:full:compile":"npm run build:compile"},"_npmUser":{"name":"curtisman","email":"curtism@microsoft.com"},"_resolved":"","_integrity":"","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"_npmVersion":"6.14.17","description":"Base views and hooks for building Fluid React views","directories":{},"sideEffects":false,"_nodeVersion":"14.20.0","dependencies":{"react":"^16.10.2","@fluidframework/map":"^0.59.2004","@fluidframework/counter":"^0.59.2004","@fluidframework/aqueduct":"^0.59.2004","@fluidframework/sequence":"^0.59.2004","@fluidframework/core-interfaces":"^0.43.1000","@fluidframework/common-definitions":"^0.20.1","@fluidframework/shared-object-base":"^0.59.2004","@fluidframework/datastore-definitions":"^0.59.2004"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"~8.6.0","rimraf":"^2.6.2","copyfiles":"^2.1.0","typescript":"~4.1.3","@types/node":"^14.18.0","@types/react":"^16.9.15","concurrently":"^6.2.0","eslint-plugin-jest":"~26.1.3","eslint-plugin-mocha":"~10.0.3","eslint-plugin-react":"~7.28.0","eslint-plugin-tsdoc":"~0.2.14","eslint-plugin-import":"~2.25.4","typescript-formatter":"7.1.0","eslint-plugin-promise":"~6.0.0","eslint-plugin-unicorn":"~40.0.0","@microsoft/api-extractor":"^7.22.2","@rushstack/eslint-config":"^2.5.1","@typescript-eslint/parser":"~5.9.0","eslint-plugin-editorconfig":"~3.2.0","@fluidframework/build-common":"^0.23.0","eslint-plugin-eslint-comments":"~3.2.0","@typescript-eslint/eslint-plugin":"~5.9.0","@fluidframework/eslint-config-fluid":"^0.28.1000"},"_npmOperationalInternal":{"tmp":"tmp/react_0.59.2004_1663280552069_0.9076837441114438","host":"s3://npm-registry-packages"}}},"time":{"created":"2021-06-16T15:29:18.633Z","modified":"2026-07-29T21:13:39.318Z","0.42.0-27549":"2021-06-16T15:29:19.155Z","0.42.0-27644":"2021-06-16T21:51:16.884Z","0.42.0-27677":"2021-06-17T14:59:41.965Z","0.42.0-27683":"2021-06-17T16:28:48.269Z","0.42.0-28410":"2021-06-25T20:52:47.359Z","0.42.0":"2021-06-26T01:53:14.634Z","0.42.1":"2021-07-08T16:28:49.021Z","0.42.2":"2021-07-08T22:53:27.436Z","0.43.0":"2021-07-09T19:51:09.553Z","0.42.3":"2021-07-09T20:56:20.915Z","0.43.1":"2021-07-19T23:13:12.559Z","0.42.4":"2021-07-20T00:14:47.785Z","0.44.0-30858":"2021-07-22T22:11:22.889Z","0.44.0":"2021-07-23T01:32:50.803Z","0.44.1":"2021-08-04T00:54:07.503Z","0.45.0-32948":"2021-08-06T19:40:45.844Z","0.45.0":"2021-08-06T23:22:11.040Z","0.45.1":"2021-08-10T01:25:12.961Z","0.45.2":"2021-08-11T22:01:43.261Z","0.45.3":"2021-08-13T00:50:19.265Z","0.45.4":"2021-08-17T18:32:48.982Z","0.46.0-34784":"2021-08-20T23:10:42.109Z","0.46.0":"2021-08-21T00:46:14.537Z","0.47.0-35912":"2021-08-31T23:53:59.138Z","0.47.0-35961":"2021-09-01T17:27:49.045Z","0.46.1":"2021-09-02T22:50:53.361Z","0.47.0-36362":"2021-09-03T18:32:08.594Z","0.47.0-36699":"2021-09-07T23:43:02.070Z","0.47.0":"2021-09-08T18:00:28.364Z","0.46.2":"2021-09-15T22:02:03.923Z","0.47.1":"2021-09-15T22:57:40.390Z","0.48.0-38105":"2021-09-20T21:22:52.231Z","0.48.0-38142":"2021-09-21T16:01:23.476Z","0.48.0":"2021-09-21T20:08:44.418Z","0.48.1":"2021-09-23T22:28:37.984Z","0.48.2":"2021-09-27T19:56:55.430Z","0.48.3":"2021-09-30T20:30:30.312Z","0.49.0-39015":"2021-09-30T22:00:47.919Z","0.48.4":"2021-10-01T17:21:03.472Z","0.49.0-39313":"2021-10-04T23:22:23.463Z","0.48.5":"2021-10-05T21:58:42.023Z","0.49.0":"2021-10-06T23:01:30.793Z","0.49.1":"2021-10-11T16:23:34.028Z","0.49.2":"2021-10-11T22:23:23.071Z","0.50.0-41365":"2021-10-21T17:02:11.000Z","0.50.0-41540":"2021-10-22T15:51:13.646Z","0.50.0":"2021-10-25T16:49:59.317Z","0.50.1":"2021-10-27T22:55:07.488Z","0.51.0-43124":"2021-11-06T00:26:32.615Z","0.51.0":"2021-11-10T22:20:31.574Z","0.50.2":"2021-11-12T21:13:13.692Z","0.51.1":"2021-11-12T22:01:32.913Z","0.50.3":"2021-11-18T21:55:48.490Z","0.51.2":"2021-11-18T22:46:52.261Z","0.52.0-44610":"2021-11-19T23:00:05.058Z","0.52.0":"2021-11-22T22:19:26.311Z","0.51.3":"2021-11-23T20:28:12.597Z","0.52.1":"2021-11-23T21:47:24.609Z","0.50.4":"2021-11-24T00:13:46.043Z","0.53.0-46105":"2021-12-08T23:06:42.852Z","0.53.0":"2021-12-10T19:00:58.279Z","0.54.0-47413":"2021-12-29T18:46:57.969Z","0.54.0":"2022-01-03T22:36:21.130Z","0.54.1":"2022-01-05T22:03:26.236Z","0.54.2":"2022-01-11T20:50:31.421Z","0.55.0-48551":"2022-01-14T21:27:08.547Z","0.55.0":"2022-01-18T23:42:58.886Z","0.55.1":"2022-01-28T19:04:35.256Z","0.56.0-49831":"2022-01-28T20:32:03.992Z","0.56.0":"2022-01-29T00:31:15.667Z","0.54.3":"2022-02-03T02:18:59.105Z","0.56.1":"2022-02-03T02:32:57.763Z","0.55.2":"2022-02-03T02:34:02.677Z","0.56.2":"2022-02-09T23:57:33.551Z","0.55.3":"2022-02-11T01:27:24.705Z","0.56.3":"2022-02-11T01:31:04.961Z","0.57.0-51086":"2022-02-12T01:11:18.316Z","0.56.4":"2022-02-17T19:58:47.739Z","0.55.4":"2022-02-17T21:14:19.607Z","0.56.5":"2022-02-19T00:41:42.880Z","0.56.6":"2022-03-02T23:11:11.139Z","0.57.0":"2022-03-02T23:58:59.163Z","0.56.7":"2022-03-03T20:15:21.740Z","0.57.1":"2022-03-04T01:45:42.113Z","0.56.8":"2022-03-04T18:46:13.540Z","0.58.0-55561":"2022-03-04T20:06:10.105Z","0.58.0-55983":"2022-03-05T00:51:42.755Z","0.56.9":"2022-03-11T21:02:41.241Z","0.57.2":"2022-03-11T23:54:55.436Z","0.58.1000":"2022-03-14T20:25:45.947Z","0.58.1001":"2022-03-18T01:43:59.356Z","0.58.2000-58133":"2022-03-18T22:13:21.292Z","0.58.2000":"2022-03-23T20:42:25.862Z","0.58.2001":"2022-03-25T01:07:13.540Z","0.58.2002":"2022-03-28T18:56:49.069Z","0.56.10":"2022-03-28T22:05:34.689Z","0.56.11":"2022-03-29T00:22:53.709Z","0.58.3000-61081":"2022-04-12T01:54:55.282Z","0.59.2000-61729":"2022-04-14T20:39:07.041Z","0.59.1000-61898":"2022-04-15T23:16:40.974Z","0.59.1000":"2022-04-16T07:34:53.839Z","0.59.1001-62246":"2022-04-18T19:09:47.828Z","0.59.2000-63294":"2022-04-22T21:10:39.542Z","0.59.2000":"2022-04-27T21:34:29.598Z","0.59.2001":"2022-05-03T21:37:55.636Z","0.59.1001":"2022-05-19T20:42:40.872Z","0.59.2002":"2022-05-19T22:46:48.468Z","0.59.2003":"2022-05-25T20:36:30.144Z","0.59.2004":"2022-09-15T22:22:32.268Z"},"bugs":{"url":"https://github.com/microsoft/FluidFramework/issues"},"author":{"name":"Microsoft and contributors"},"license":"MIT","homepage":"https://fluidframework.com","repository":{"url":"git+https://github.com/microsoft/FluidFramework.git","type":"git","directory":"experimental/framework/react"},"description":"Base views and hooks for building Fluid React views","maintainers":[{"email":"ffsa@microsoft.com","name":"ms-fluid-bot"},{"email":"microsoft-oss-publishing@microsoft.com","name":"microsoft-oss-releases"},{"email":"npmjs@microsoft.com","name":"microsoft1es"}],"readme":"# @fluid-experimental/react\n\nThe Fluid Framework's React package enables React developers to quickly start building large, scalable React apps with synced views powered by Fluid data. It does this by providing a `SyncedDataObject`, Fluid React hooks and a base view class for building React views that use synced states provided by Fluid.\n\nExamples on how to use all of the different tools in this package can be found in the [clicker-react](../../../examples/data-objects/clicker-react) and [`likes`](../../../examples/data-objects/likes) folders under `./examples/data-object/` from the Fluid Framework repo root.\n\nA good general order of operations to follow when writing a `SyncedDataObject` implementation is the following:\n1. Define the DDSes needed in the `SyncedDataObject`constructor. This can be done using the `set*Config` functions.\n2. Fill in the `render(element: HTMLElement)` function with a call to `ReactDOM.render` and pass in a React view.\n3. Build out the React view either using the `FluidReactView` class or as a functional view using the various hooks that are available.\n\n## SyncedDataObject\n\nThe ['SyncedDataObject'](./src/syncedDataObject.ts) is an extension of the Fluid `DataObject` class provided by the `@fluidframework/aqueduct` package. This will be the base class that users developing Fluid objects with React views will want to extend.\n\nThe `SyncedDataObject` essentially acts as the data store for the React app that is rendered within it. It provides a configuration where we can set up the schema for our data store, and also guarantees that all the values defined in the configuration will be automatically initialized prior to the view rendering. It also ensures that all updates that were made in the data store for that session will be automatically restored when the app is loaded fresh again by another client for that session. Finally, and most importantly, it guarantees that all updates that are made in the data store are synced live with all other clients that are currently viewing this React app in the current session, without requiring event listeners or component lifecycle methods.\n\n`SyncedDataObject` uses the same factory as `DataObject`. However, in addition, it also provides the following functionality:\n\n- A `syncedStateConfig` where users can define the different types of values that they would like to see prepared for their view to consume. Values defined here are guaranteed to be initialized and available prior to `render` being called. Here, users can assign the DDSes that their React views will need by using the pre-built helper functions available to them from the `syncedObjects` folder, i.e. `setSyncedStringConfig`, etc. or they can manually define their own unique configuration with `this.setConfig`. Each value set on the config will have its own `syncedStateId` which we will use to refer to the prepared value from the view.\n\n- A `fluidObjectMap` that guarantees that all Fluid DDSes/objects used by this `SyncedDataObject` will be automatically created and loaded without the need for component lifecycle methods such as `initializeStateFirstTime` and `initializeStateFromExisting`\n\n#### SyncedDataObject Example\n\n```jsx\nexport class Likes extends SyncedDataObject {\n    constructor(props) {\n        super(props);\n        // Adds a synced counter to config under ID 'likes'\n        setSyncedCounterConfig(\n            this,\n            \"likes\",\n        );\n        // Adds a synced string to config under ID 'imgUrl'\n        setSyncedStringConfig(\n            this,\n            \"imgUrl\",\n            defaultImgUrl,\n        );\n    }\n}\n```\n\n## syncedObject Hooks\n\nThe [synced Objects folder](./src/syncedObjects) contains a collection of setSynced\\*Config helper function and useSynced\\* hook pairings. These functions allow you to easily start using the DDSes that were prepared in the `syncedDataObject`. Since these are all wrappers around the `React.useState` hook, with added syncing functionality, simply calling these functions using regular React hook rules will automatically convert the function into a React functional view.\n\nEach of these helper & hook pairings take a Fluid DDS and binds them to the `syncedDataObject` under the unique `syncedStateId`. This ensures that any changes that happen on these DDSes see synced updates show up live on all connected React views.\n\nA single `syncedDataObject` can hold multiple different types of DDSes and other `DataObjects` under different `syncedStateIds`. However, each unique ID is exclusive to the type of value that is set there when using the helper & hook pairs.\n\nThe current roster of available helper & hook pairs for different DDSes are:\n- For just setting type T objects on a `SharedMap` -> `useSyncedObject<T>` & `setSyncedObjectConfig<T>`\n- `SharedCounter` -> `useSyncedCounter` & `setSyncedCounterConfig`\n- `SharedString` -> `useSyncedString` & `setSyncedStringConfig`\n\nNOTE: IT IS IMPORTANT TO PICK THE RIGHT DDS.\n\nWhile it may be tempting to use `useSyncedObject` for any type T object, a SharedMap has different syncing logic from a SharedString, etc. Therefore, passing a string to be set on a SharedMap is not the same as using a SharedString. The latter contains additional logic that allows people to, for example, type on the same word together without overwriting one another's characters. Please use `useSyncedString` for these scenarios. The SharedMap, on the other hand, is useful for having a distributed dictionary of items that are always in sync based on the values being set on the map.\n\n### syncedObject Hooks Example\n\nThis example provides the view for the `syncedDataObject` in the example above.\n\n```jsx\nfunction LikesView(\n    props: ILikesViewProps,\n) {\n    // Use the synced states that were prepared on our syncedDataObject above using the setSynced*Config helper functions. Note that the useSynced* function and the ID passed in correspond to the how the config was set above. These values are guaranteed to be available in the view\n    const [likes, likesReducer] = useSyncedCounter(props.syncedDataObject, \"likes\");\n    const [imgUrl, setImgUrl] = useSyncedString(props.syncedDataObject,\"imgUrl\");\n\n    // The remaining code is for rendering the React view elements themselves\n\n    // Render\n    return (\n        <div>\n            <div>\n                <img width='100%' src={imgUrl?.getText()}/>\n                {imgUrl !== undefined\n                    ? <CollaborativeInput\n                        style={{ width: \"90%\" }}\n                        sharedString={imgUrl}\n                        onInput={(value: SharedString) => setImgUrl({ value })}\n                    />\n                    : undefined}\n            </div>\n            <span>\n                {`Likes: ${likes}`}\n            </span>\n            <button onClick={() => likesReducer.increment(1)}>\n                {\"+\"}\n            </button>\n        </div>\n    );\n}\n```\n\nPlease note that for every different type of DDS that is used, the initial factory for the data object needs to be updated to include those dependencies. Each DDS only needs to be added to the factory dependency list once, regardless of how many different ways it is used.\n\nI.e. for the example above,\n```jsx\n// ----- FACTORY SETUP -----\nexport const LikesInstantiationFactory = new DataObjectFactory(\n    \"likes\",\n    Likes,\n    [\n        SharedCounter.getFactory(),\n        SharedString.getFactory(),\n    ],\n    {},\n);\nexport const fluidExport = LikesInstantiationFactory;\n```\n\nThese hooks should allow for general functionality to start users off building synced React views using Fluid DDSes. However, if users would like to set up their own custom relationships and configurations, we do also offer the `FluidReactView` base class that extends `React.Component` for classical views, and the `useStateFluid` and `useReducerFluid` hooks for functional views.\n\n## FluidReactView\n\nThis is the base level `FluidReactView` that offers a synced view state and a mapping between the view and synced state stored in the root. It allows users to set up their own unique relationships between the Fluid state, which is the data state that is being synced, and the view state, which extracts the synced data from the Fluid state in a format that is ready for consumption in the view. In many cases, these the Fluid and view state can be identical but, in more complex scenarios, it may be beneficial to have a cleaner view state that picks out the relevant values needed for the view from the Fluid state.\n\nTo see an example of this, please take a look at the `@fluid-example/clicker-react` example. If we take a look at this example in parts, we can see how the `SyncedDataObject` that houses the view aligns with the `FluidReactView` that is rendered.\n\nLooking at the constructor, we see that a configuration is created under ID `clicker` containing a SharedCounter:\n```jsx\n    constructor(props) {\n        super(props);\n        // Mark the counter value in the state as a SharedCounter type and pass in its create function\n        // so that it will be created on the first run and be available on our React state\n        // We also mark the \"incremented\" event as we want to update the React state when the counter\n        // is incremented to display the new value\n        this.setConfig<ICounterState>(\n            \"clicker\",\n            {\n                syncedStateId: \"clicker\",\n                fluidToView: new Map([\n                    [\n                        \"counter\", {\n                            type: SharedCounter.name,\n                            viewKey: \"counter\",\n                            sharedObjectCreate: SharedCounter.create,\n                            listenedEvents: [\"incremented\"],\n                        },\n                    ],\n                ]),\n                defaultViewState: {},\n            },\n        );\n    }\n```\n\nEach `FluidReactView` is bound to its unique state ID and will automatically re-render when any values within the synced state change, without needing any additional event listeners or component lifecycle methods. However, since we are defining our own custom relationships now, we are not limited to only one DDS/Fluid object per `syncedStateId` like we were with the helper & hook pairs above. We can define multiple different DDS/Fluid objects per `syncedStateId` by adding keys to the `fluidToView` map. If we wanted a second counter, the configuration would look like this:\n\n```jsx\nthis.setConfig<ICounterState>(\n    \"clicker\",\n    {\n        syncedStateId: \"clicker\",\n        fluidToView: new Map([\n            [\n                \"counter\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n                 \"counter2\", {\n                    type: SharedCounter.name,\n                    viewKey: \"counter2\",\n                    sharedObjectCreate: SharedCounter.create,\n                    listenedEvents: [\"incremented\"],\n                },\n            ],\n        ]),\n        defaultViewState: {},\n    },\n);\n```\n\nThis is then passed to the `FluidReactView` in the render function with the `clicker` ID:\n```jsx\npublic render(element: HTMLElement) {\n    ReactDOM.render(\n        <CounterReactView\n            syncedStateId={\"clicker\"}\n            syncedDataObject={this}\n        />,\n        element,\n    );\n    return element;\n}\n```\n\nAnd now if we look at the view itself, we will see that the state is now pre-loaded with the SharedCounter under the key `counter`, as we defined in the synced state configuration.\n\n```jsx\nclass CounterReactView extends FluidReactView<CounterViewState, CounterFluidState> {\n    constructor(props) {\n        super(props);\n        this.state = {};\n    }\n\n    render() {\n        return (\n            <div>\n                <span className=\"value\">\n                    {this.state.counter?.value}\n                </span>\n                <button onClick={() => { this.state.counter?.increment(1); }}>+</button>\n            </div>\n        );\n    }\n}\n```\n\nThis state will automatically update for all clients when `counter?.increment(1)` is called, and trigger a re-render for everyone. No additional event listeners are required.\n\nIf we wanted to access our second counter, we can simply do `this.state.counter2?.value`.\n\n## useStateFluid\n\nThis is analogous to the React view but as a functional hook. Users can similarly use the returned setState callback to perform synced updates to both their local and synced states.\n\nSome of the `useSynced*` hooks that we discussed above use this underlying call to power their DDS-specifc behavior. It can also be used directly.\n\n## useReducerFluid\n\nThis is the hook of choice for larger-scale applications that require more complex mutations, need to work with multiple Fluid objects, and need to have a division between data and view models. Here, instead of having only the view state to manipulate, users have both the view state and the Fluid state, with the former containing primitives used for rendering and the latter containing Fluid views to manipulate data in a synced manner. This hook also introduces the concept of a local FluidObjectMap that stores and listens to changes on already fetched Fluid objects.\n\n\nReducers offer ways of mutating the state whereas selectors offer ways of fetching data from other Fluid objects. When either involves the addition of new Fluid objects locally, these are added to the FluidObjectMap so that they can be accessed by the view synchronously.\n\n\nAny updates to the root state are converted to updates in the view using the provided fluidConverters in the fluidToView map, and vice versa. This allows changes locally to reflect on the root, and root changes to also be translated back to local state updates.\n\nThis is currently used to power the `useSyncedCounter` hook, and an example showcasing how to use it is coming soon.\n\n## createContextFluid\n\nThis hook is for users who want to be able to easily create a context with provider and consumer that pass the root and initial state through their app.\n\n\nThis hook calls useStateFluid and returns the state and setState values back to be used as part of the initial values passed down by the provider and used by a consumer\n\n\nEssentially, this allows that portion of the root state to be manipulated through different levels of a React app, giving apps the ability to have multiple different views manipulate the same data not only throughout the app but through all renders of the app on different clients\n\n## Trademark\n\nThis project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks\nor logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\n","readmeFilename":"README.md"}