'use client'; import { State, ActionTypes, Controller, createReducer, } from '@data-client/core'; import React, { useEffect, useState, useMemo, useRef, useCallback, } from 'react'; import type { DevToolsPosition } from '../../components/DevToolsButton.js'; import { renderDevButton } from '../../components/renderDevButton.js'; import { ControllerContext, StoreContext, BackupLoading, UniversalSuspense, StateContext, } from '../../index.js'; interface Store { subscribe(listener: () => void): () => void; getState(): S; } interface Props { children: React.ReactNode; store: Store; selector: (state: S) => State; controller: Controller; devButton?: DevToolsPosition | null | undefined; hasDevManager?: boolean; } /** * Like DataProvider, but for an external store * @see https://dataclient.io/docs/api/ExternalDataProvider */ export default function ExternalDataProvider({ children, store, selector, controller, devButton = 'bottom-right', hasDevManager = false, }: Props) { const masterReducer = useMemo(() => createReducer(controller), [controller]); const selectState = useCallback(() => { const state = selector(store.getState()); return state.optimistic.reduce(masterReducer, state); }, [masterReducer, selector, store]); const [state, setState] = useState(selectState); const isMounted = useRef(true); useEffect(() => { isMounted.current = true; return () => { isMounted.current = false; }; }, []); useEffect(() => { const unsubscribe = store.subscribe(() => { if (isMounted.current) setState(selectState()); }); return unsubscribe; }, [selectState, store]); const adaptedStore = useMemo(() => { return { ...store, getState: () => selector(store.getState()) }; }, [selector, store]); return ( }> {children} {renderDevButton(devButton, hasDevManager)} ); }