Class ClassComponent<TProps>

In essence, this is a class wrapper around an underlying function component. It acts as syntactic sugar, allowing you to create a regular function component, while writing in an object-oriented format.

This is designed to closely resemble the old React.Component class, making it easier to migrate older class components to the newer hooks-based system with little to no changes to their existing semantics/implementation.

Type Parameters

Hierarchy (View Summary)

Constructors

Properties

extract: Extractor = ...

Extract a Function Component (FC) which can be used to render your ClassComponent just like any other React component.

Each JSX reference to the returned component will render with a separate instance of your class.

So you only need to call YourClassComponent.extract() (or *.FC()) once, then use the returned function component as many times as you need.

It is recommended to store this returned value as a static member of your ClassComponent. While this value may be given any name, the name RC (for "React Component") is the recommended convention.

class Button extends ClassComponent {
static readonly RC = this.extract(); // or this.FC();
// Because of the static keyword, `this` here refers to the class itself, same as calling `Button.extract()`.
}

// Render with `<Button.RC />`, or export RC to use the component in other files.
export default Button.RC;
FC: Extractor = ...
template: (context: void | object) => null | Element = ...

Analogous to React.Component.render. A function that returns your component's JSX template.


Ideally the template method should only be concerned with defining the HTML/JSX structure of your component's UI.

If you need to transform some data for display, do so in [beforeRender](ComponentInstance.beforeRender), and return an object with transformed data that can be rendered directly.

The returned object will be passed to your template method as a context object.


beforeRender = () => {
return {
title: `My Site | ${this.props.title}`,
};
}
template = (ctx) => {
return (
<h1>
{ctx.title}
</h1>
<p>{this.props.description}</p>
);
}
forceUpdate: VoidFunction

Manually trigger a rerender of your component. You should rarely ever need this. But if you are migrating an older React.Component class, this should provide similar functionality to the React.Component.forceUpdate | forceUpdate method provided there.

Note that the callback argument is currently not supported.

beforeMount: IVoidFunction = ...

Runs only before the first render, i.e before the component instance is mounted.

It is ignored on subsequent rerenders. Uses useMemo() under the hood.

PS: You can conditionally update state from here, but with certain caveats. See the React docs for details.

onMount: AsyncAllowedEffectCallback = ...

Runs only after the first render, i.e after the component instance is mounted. It is ignored on subsequent rerenders.

Should usually only be used for logic that does not directly take part in determining what to render, like logging and analytics.

A cleanup function.

Uses useEffect() under the hood.

beforeRender: () => void | object = ...

Runs before every render cycle, including the first. Useful for logic that is involved in determining what to render.

It runs in the same way as logic placed directly into the function component body preceding the return statement.

This is the ideal place to transform data for display. Return the transformed data in an object, and the object will availble as [self.templateContext](templateContext) for use in your JSX template.

PS: You can conditionally update state from here, but with certain caveats. See the React docs for details.

onRender: AsyncAllowedEffectCallback = ...

Runs after every render cycle, including the first.

Should usually only be used for logic that does not directly take part in determining what to render, like logging and analytics.

Uses useEffect() under the hood.

A cleanup function.

cleanUp: IVoidFunction = ...

Runs when the component is unmounted. It is called after the cleanup function returned by onMount.

state: TCleanState<object>

A CleanState object. Holds all of your component's state, and methods for conveniently manipulating those values. Initialiazed with the object returned from your getInitialState method.

props: TProps extends null ? EmptyObject : TProps

The props passed into your component at the time of rendering.

hooks: void | object

Values received from the hooks your component consumes. This holds the latest copy of the object returned by useHooks.

You should not mutate this object directly. useHooks is called on every render and the object it returns will completely overwrite whatever the previous value of this.hooks was. this.hooks should only be updated through the object returned from useHooks.

If you need to update a value from outside useHooks as well, then consider wrapping it with React.useRef, since React refs are persisted across rerenders.

_hmrPreserveKeys: (string & {} | (keyof ClassComponent<TProps>))[] = []

Persist class members during HMR. # Persist Class Members During HMR Specify custom class members to be copied over whenever the class is reinstantiated during hot module replacement.

Oore handles HMR by recreating the class instance with the updated code whenever there is a file change. Your component is then rerendered so that event handlers now point to the new functions.

For this to work well, your component's state needs to be preserved, so it is copied over from the old instance, to the newly created one. This includes state, props, & hooks by default, but you can extend it to include more properties if there are values your component expects to be persistent.

In most cases, any values you wish to preserve should be created in useHooks with React.useRef. Like so:

class MyClass extends ComponentLogic {
// Note: `useHooks` is not available in `ComponentMethods` / `useMethods`.
// If you're using `useMethods`, switch to `useLogic` + `ComponentLogic`
// to access the `useHooks` method.
// PS: To add refs as instance properties in ComponentMethods, create the ref in the function component's body
// and assign it to the appropriate instance member. Keep scrolling for an example.
useHooks = () => {
// You can use an instance property.
this.myAttr = useRef('my-attribute');

// Or add it to the `this.hooks` object.
return {
inputId: useRef('input-id'),
};
};

myMethod = () => {
this.myAttr.current = 'new-value';
this.hooks.inputId.current = 'new-value';

console.log({
classMember: this.myAttr.current,
hooksObject: this.hooks.inputId.current,
});
};
}

const MyInput = (props) => {
const self = useLogic(MyClass, props);

// Or, with useMethods...
const methods = useMethods(/* ...args */);
methods.myAttr = useRef('input-id');

return (
<input
id={self.hooks.inputId.current}
data-attr={self.myAttr.current}
// Or:
data-attr2={methods.myAttr.current}
/>
);
}

If you use a ref in this way, React will preserve it for you, and there will be no need to use _hmrPreserveKeys.

_hmrPreserveKeys is only relevant in development and has not effect in production environment. Accordingly, you should only create this array when environment is development, so that it can be tree-shaken during production builds.

@example Specify additional properties to be considered stateful, in addition to state, props, and hooks.

MyComponentMethods extends ComponentMethods {
// Some class member definitions...

constructor() {
if (process.env.NODE_ENV === 'development') {
this._hmrPreserveKeys = ['inputId', 'unsubscribeCallback'];
}
}

// Method definitions...
}

With the above example, whenever HMR occurs, this.inputId and this.unsubscribeCallback will maintain there existing values, just like state, props, and hooks. Meanwhile everything else will be recreated.

PS: Since the code is written in an environment condition, it should naturally be stripped from the production build to avoid shipping dead code.

_onHmrUpdate?: <TInstance extends ClassComponent<TProps>>(
    oldInstance: TInstance,
) => void

Run custom logic after HMR update. # Custom HMR Handling Handle complex update logic whenever your component instance is updated through HMR. This feature is available on all component logic classes, including ComponentMethods.

This function is called on the new instance, and it receives the old instance as its only argument. So you can access data from the old instance, and reinitialize any processes on the new instance as needed.

_onHmrUpdate is only relevant in development and has not effect in production environment. Accordingly, you should only assign this function when environment is development, so that it can be tree-shaken during production builds.

@example

class MyComponentLogic extends ComponentLogic {
// Some class member definitions...

constructor() {
if (process.env.NODE_ENV === 'development') {
this._onHmrUpdate = (oldInstance) => {
// Your custom hmr logic here.
};
}
}

// Method definitions...
}

Accessors

  • get templateContext(): ReturnType<this["beforeRender"]>
  • Exposes the object returned by beforeRender.

    This is useful when you need to render some state or props in a transformed format. Put the transformation logic in beforeRender to the keep the main function component body clean.


    Returns ReturnType<this["beforeRender"]>

    class MyComponentLogic extends ComponentInstance {
    beforeRender = () => {
    const title = `My Site | ${this.props.title}`;
    return { title };
    }
    }
    const MyComponent = (props) => {
    const self = useInstance(MyComponentLogic, props);
    const { templateContext: ctx, state } = self;

    return (
    <h1>
    {ctx.title}
    </h1>
    <p>{props.description}</p>
    );
    }

Methods

  • Called before each instance of your component is mounted. It receives the initial props object and should return an object with the initial values for your component's state.

    Parameters

    Returns object

  • Call React hooks from here. If your component needs access to values return from the hooks you call, expose those values by returning an object with said values.

    The returned object will be accessible as this.hooks within your component class.

    Returns void | object