This commit is contained in:
@@ -97,11 +97,11 @@ export const isAsync = (u: unknown): u is Async => Predicate.hasProperty(u, Asyn
|
|||||||
* Note: The component cannot have a prop named "promise" as it's reserved for internal use.
|
* Note: The component cannot have a prop named "promise" as it's reserved for internal use.
|
||||||
*
|
*
|
||||||
* @param self - The component to convert to an Async component
|
* @param self - The component to convert to an Async component
|
||||||
* @returns A new Async component with the same body, error, and context types as the input
|
* @returns A new `Async` component with the same body, error, and context types as the input
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const MyAsyncComponent = Component.make("MyComponent")(function*() { ... }).pipe(
|
* const MyAsyncComponent = MyComponent.pipe(
|
||||||
* Async.async,
|
* Async.async,
|
||||||
* )
|
* )
|
||||||
* ```
|
* ```
|
||||||
@@ -141,18 +141,16 @@ export const async = <T extends Component.Component<any, any, any, any>>(
|
|||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* // Curried
|
* // Curried
|
||||||
* const MyAsyncComponent = Component.make("MyComponent")(function*() { ... }).pipe(
|
* const MyAsyncComponent = MyComponent.pipe(
|
||||||
* Async.async,
|
* Async.async,
|
||||||
* Async.withOptions({ defaultFallback: <p>Loading...</p> }),
|
* Async.withOptions({ defaultFallback: <p>Loading...</p> }),
|
||||||
* )
|
* )
|
||||||
*
|
*
|
||||||
* // Uncurried
|
* // Uncurried
|
||||||
* const MyAsyncComponent = Component.make("MyComponent")(function*() { ... }).pipe(
|
* const MyAsyncComponent = Async.withOptions(
|
||||||
* Async.async,
|
* Async.async(MyComponent),
|
||||||
|
* { defaultFallback: <p>Loading...</p> },
|
||||||
* )
|
* )
|
||||||
* const MyAsyncComponentWithOptions = Async.withOptions(MyAsyncComponent, {
|
|
||||||
* defaultFallback: <p>Loading...</p>,
|
|
||||||
* })
|
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export const withOptions: {
|
export const withOptions: {
|
||||||
|
|||||||
@@ -511,7 +511,7 @@ export const makeUntraced: (
|
|||||||
* const MyComponentWithCustomOptions = MyComponent.pipe(
|
* const MyComponentWithCustomOptions = MyComponent.pipe(
|
||||||
* Component.withOptions({
|
* Component.withOptions({
|
||||||
* finalizerExecutionStrategy: ExecutionStrategy.parallel,
|
* finalizerExecutionStrategy: ExecutionStrategy.parallel,
|
||||||
* finalizerExecutionDebounce: "50 millis"
|
* finalizerExecutionDebounce: "50 millis",
|
||||||
* })
|
* })
|
||||||
* )
|
* )
|
||||||
* ```
|
* ```
|
||||||
|
|||||||
@@ -4,41 +4,18 @@ import * as React from "react"
|
|||||||
import type * as Component from "./Component.js"
|
import type * as Component from "./Component.js"
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A unique symbol representing the Memoized component type.
|
|
||||||
* Used as a type brand to identify Memoized components.
|
|
||||||
*
|
|
||||||
* @experimental
|
|
||||||
*/
|
|
||||||
export const MemoizedTypeId: unique symbol = Symbol.for("@effect-fc/Memoized/Memoized")
|
export const MemoizedTypeId: unique symbol = Symbol.for("@effect-fc/Memoized/Memoized")
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the Memoized type ID symbol.
|
|
||||||
*/
|
|
||||||
export type MemoizedTypeId = typeof MemoizedTypeId
|
export type MemoizedTypeId = typeof MemoizedTypeId
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Memoized component that uses React.memo to optimize re-renders based on prop equality.
|
* A trait for `Component`'s that uses `React.memo` to optimize re-renders based on prop equality.
|
||||||
* Combines Component behavior with Memoized-specific options.
|
|
||||||
*
|
*
|
||||||
* @template P The props type of the component
|
* @template P The props type of the component
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```ts
|
|
||||||
* const MyComponent = component({ ... })
|
|
||||||
* const MemoizedComponent = memoized(MyComponent)
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
export interface Memoized<P> extends MemoizedPrototype, MemoizedOptions<P> {}
|
export interface Memoized<P> extends MemoizedPrototype, MemoizedOptions<P> {}
|
||||||
|
|
||||||
/**
|
|
||||||
* The prototype object for Memoized components containing their methods and behaviors.
|
|
||||||
*/
|
|
||||||
export interface MemoizedPrototype {
|
export interface MemoizedPrototype {
|
||||||
/**
|
|
||||||
* The Memoized type ID brand.
|
|
||||||
*/
|
|
||||||
readonly [MemoizedTypeId]: MemoizedTypeId
|
readonly [MemoizedTypeId]: MemoizedTypeId
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,27 +34,9 @@ export interface MemoizedOptions<P> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The prototype object for Memoized components.
|
|
||||||
* Provides the `transformFunctionComponent` method for memoizing React function components.
|
|
||||||
*
|
|
||||||
* @internal Use the `memoized` function to create Memoized components instead of accessing this directly.
|
|
||||||
*/
|
|
||||||
export const MemoizedPrototype: MemoizedPrototype = Object.freeze({
|
export const MemoizedPrototype: MemoizedPrototype = Object.freeze({
|
||||||
[MemoizedTypeId]: MemoizedTypeId,
|
[MemoizedTypeId]: MemoizedTypeId,
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms a React function component by wrapping it with React.memo.
|
|
||||||
*
|
|
||||||
* @param f - The React function component to memoize
|
|
||||||
* @returns A memoized version of the component that uses the configured propsEquivalence function
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```ts
|
|
||||||
* const MemoizedComponent = memoized(MyComponent)
|
|
||||||
* const Fn = MemoizedComponent.transformFunctionComponent((props) => <div>{props.x}</div>)
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
transformFunctionComponent<P extends {}>(
|
transformFunctionComponent<P extends {}>(
|
||||||
this: Memoized<P>,
|
this: Memoized<P>,
|
||||||
f: React.FC<P>,
|
f: React.FC<P>,
|
||||||
@@ -87,37 +46,19 @@ export const MemoizedPrototype: MemoizedPrototype = Object.freeze({
|
|||||||
} as const)
|
} as const)
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A type guard to check if a value is a Memoized component.
|
|
||||||
*
|
|
||||||
* @param u - The value to check
|
|
||||||
* @returns `true` if the value is a Memoized component, `false` otherwise
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```ts
|
|
||||||
* if (isMemoized(component)) {
|
|
||||||
* // component is a Memoized component
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export const isMemoized = (u: unknown): u is Memoized<unknown> => Predicate.hasProperty(u, MemoizedTypeId)
|
export const isMemoized = (u: unknown): u is Memoized<unknown> => Predicate.hasProperty(u, MemoizedTypeId)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a Component into a Memoized component that optimizes re-renders using React.memo.
|
* Converts a Component into a `Memoized` component that optimizes re-renders using `React.memo`.
|
||||||
*
|
|
||||||
* The resulting component will use React.memo to skip re-renders when props haven't changed,
|
|
||||||
* based on the configured equivalence function (or the default equality check).
|
|
||||||
*
|
*
|
||||||
* @param self - The component to convert to a Memoized component
|
* @param self - The component to convert to a Memoized component
|
||||||
* @returns A Memoized component with the same body, error, and context types as the input
|
* @returns A new `Memoized` component with the same body, error, and context types as the input
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const MyComponent = component({
|
* const MyMemoizedComponent = MyComponent.pipe(
|
||||||
* body: (props) => // ...
|
* Memoized.memoized,
|
||||||
* })
|
* )
|
||||||
*
|
|
||||||
* const MemoizedComponent = memoized(MyComponent)
|
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export const memoized = <T extends Component.Component<any, any, any, any>>(
|
export const memoized = <T extends Component.Component<any, any, any, any>>(
|
||||||
@@ -141,16 +82,17 @@ export const memoized = <T extends Component.Component<any, any, any, any>>(
|
|||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const MemoizedComponent = memoized(MyComponent)
|
* // Curried
|
||||||
|
* const MyMemoizedComponent = MyComponent.pipe(
|
||||||
|
* Memoized.memoized,
|
||||||
|
* Memoized.withOptions({ propsEquivalence: (a, b) => a.id === b.id }),
|
||||||
|
* )
|
||||||
*
|
*
|
||||||
* // Uncurried
|
* // Uncurried
|
||||||
* const configured = withOptions(MemoizedComponent, {
|
* const MyMemoizedComponent = Memoized.withOptions(
|
||||||
* propsEquivalence: (a, b) => a.id === b.id
|
* Memoized.memoized(MyComponent),
|
||||||
* })
|
* { propsEquivalence: (a, b) => a.id === b.id },
|
||||||
*
|
* )
|
||||||
* // Curried
|
|
||||||
* const configurer = withOptions({ propsEquivalence: (a, b) => a.id === b.id })
|
|
||||||
* const configured = configurer(MemoizedComponent)
|
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export const withOptions: {
|
export const withOptions: {
|
||||||
|
|||||||
Reference in New Issue
Block a user