Files
brave-core/components/common/api/react_api.tsx
T
Pete Miller 3e5b3073de createInterfaceApi - a browser proxy store (#29601)
* createInterfaceApi - a browser proxy store

A way to generate a state store browser proxy from any remote API, designed for mojom (or extension) APIs

The intention is to create something requiring minimal configuration and remove often repeated patterns to do with WebUI <-> Browser communication and
- storing and caching fetch results across UI components
- event listening to data updates and upadting local cache
- passing through actions to UI components
- mocking data for unit tests and storybook

This is not intended to replace all local state management. The intention is to manage only the data that is owned by a remote (e.g. the browser) and present it to the UI. Any local-only state should still be managed by the relevant framework (i.e. React, Lit, or Svelte) or a separate state manager.
However, this does add another relevant utility:

Used to send an instance of an api to a tree of React components. However, since it accepts any function it can also conveniently be used to store global state for that tree, using React hooks (`useState`, `useMemo`) or derived state from values retrieved from the API.
React Context is not that performant, as the whole tree will re-render when any pieces of state change, so this should be used sparingly. Instead, hooks for each individual endpoint should be used via the API instance which is passed down via Context. The API instance itself won't change and cause re-renders, but the individual hooks will cause subscriptions to be made to each piece of data.

A helper to generate endpoints from an interface. Prevents having to re-declare the mojom function signature as Typescript can deduce it from the generated mojom JS.

A helper to mark an interface function as an 'event'. Prevents having to re-declare the mojom function signature as Typescript can deduce it from the generated mojom JS.

See `components/common/api/readme.md` for examples.
2026-02-08 15:54:40 -08:00

55 lines
1.7 KiB
TypeScript

// Copyright (c) 2025 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
import * as React from 'react'
/**
* Props for the generated Provider component.
* Includes the API props plus an optional `overrides` prop for testing/storybook.
*/
type ProviderProps<API, T> = React.PropsWithChildren<
API & {
/**
* Optional overrides to merge with the hook's result.
* Useful for Storybook/tests to override specific values
* (like `isFeedbackFormVisible`) while using real provider logic.
*/
overrides?: Partial<T>
}
>
// Provide a React Context provider and useAPI hook which the consumer
// can use to get the API instance and the implementer
// can customize with the required arguments for the provider.
// Optionally specify a hook to transform the API instance before returning it.
export default function generateReactContextForAPI<API extends {}, T = API>(
hook: (api: API) => T = (api: API) => api as unknown as T,
) {
const Context = React.createContext<T | null>(null)
function useAPI() {
const maybeAPI = React.useContext(Context)
if (!maybeAPI) {
throw new Error('useAPI must be used within a Provider')
}
return maybeAPI
}
function Provider(props: ProviderProps<API, T>) {
const { overrides, children, ...apiProps } = props
const value: T = hook(apiProps as API)
// Merge overrides if provided (shallow merge)
const finalValue = overrides ? { ...value, ...overrides } : value
return <Context.Provider value={finalValue}>{children}</Context.Provider>
}
return {
useAPI,
Provider,
}
}