I have an application where one of its components is responsible for creating and managing a tree. Other components in this application sometimes need the tree data.
One of the available solutions for using the tree data in other components is to use provide/inject.
I wanted to see what the necessity of using this method is, given that I can access the tree data and see changes by using the class below and calling the useTreeIndex function in any component
import { createGlobalState } from '@vueuse/core'
import { isUndefined } from '@sindresorhus/is'
import type { ISimpleTreeActionable } from '@/types/baseModels'
import { SimpleTreeModel } from '@/types/baseModels'
export const useSelectedNode = createGlobalState(
() => {
const simpleTreeModelStored = reactive<SimpleTreeModel>(new SimpleTreeModel())
return { simpleTreeModelStored }
},
)
const treeData = reactive<ISimpleTreeActionable[]>([])
const treeIndex = reactive<Record<number, ISimpleTreeActionable>>({})
export function useTreeData() {
return treeData
}
export function useTreeIndex() {
return treeIndex
}
export function addNode(nodeItem: ISimpleTreeActionable): boolean {
if (nodeItem.parentId === -1)
return false
if (isUndefined(treeIndex[nodeItem.parentId].children))
treeIndex[nodeItem.parentId].children = []
treeIndex[nodeItem.parentId].children?.push(nodeItem)
treeIndex[nodeItem.id] = nodeItem
return true
}
I use the following code in another component and have access to the complete tree data.
const treeIndexStore = useTreeIndex()
What is the best method for using and working with data that is needed and used in several other components
This is a simple implementation of global state described in the documentation. A lot of times it works, and there may be no need to look further. It's beneficial to wrap it with composable-like functions like useTreeData
because this allows to change the implementation later without refactoring the application.
provide
/inject
additionally do this:
keep state local to the component hierarchy
provide a mechanism for dependency injection based on the component hierarchy (beneficial for testing, not so much with Jest/Vitest module mocking)
Given there are several sibling component trees, they can have independent states if necessary, or a component can override parent state for its children.
The downside of provide
/inject
strictly follows the rules of composables and can be only used only in the body of setup
, this limits the usage in non-component context (services, router hooks, etc).
The upside is that it naturally prevents the state from leaking outside root component. This would be a major problem for a simple global state because it's incompatible with SSR; each application instance on server side should have its own state, which is impossible when a state is a singleton.
Pinia is commonly considered a good choice for global state, particularly it doesn't have these drawbacks. It can be used inside and outside the components because it doesn't rely on provide
/inject
, yet Pinia instance is tied to application instance and safe use in SSR context.