npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

class-based-react-hooks

v0.1.2

Published

Create React hooks using classes - with hookDidMount, hookWillUpdate, etc.

Downloads

7

Readme

Class-Based React Hooks

Create React hooks using classes - with hookDidMount, hookWillUpdate, etc.

Examples

See demos on CodeSandbox.

useCounter

// Import these.
import { ReactHook, createHook } from 'class-based-react-hooks';

// Define the type of the arguments of your hook.
type Args = [number];

// Define the type of the state of your hook.
type State = {
	num: number;
};

// Define the return type of your hook.
type Return = {
	num: number;
	increment: (by?: number) => void;
	reset: () => void;
};


// Start defining the class. Use the types we defined above as generic arguments for `ReactHook`.
class UseCounter extends ReactHook<Args, State, Return> {
	// Make your constructor. It must take Args as argument.
	constructor(args: Args) {
		// For React components you would do this.state = {...}.
		// Here, you pass the state to super.
		super(args, {
			num: args[0],
		});
	}
	// render() must return the hook's return type.
	render(): Return {
		return {
			num: this.state.num,
			increment: this.increment,
			reset: this.reset,
		};
	}
	increment = (by: number = 1) => {
		// setState will update the hook's state and schedule a rerender.
		this.setState({
			num: this.state.num + by,
		});
	};
	reset = () => {
		this.setState({
			num: this.args[0],
		});
	};
	
}

// Use createHook to turn your class into a hook.
const useCounter = createHook(UseCounter);


// Now use the hook you just created.
function Counter() {
	const {num, increment, reset} = useCounter(1);
	return (
		<div>
			<span>{num}</span>
			<button onClick={increment}>Increment</button>
			<button onClick={reset}>Reset</button>
		</div>
	)
}

useMiniSWR

The main idea of this is similar to useSWR, but with only the most basic features implemented.

  • It is faster!! SWR starts data fetching after the component has mounted (or updated). MiniSWR starts immediately as it renders. 🔥🔥🔥
  • I find this class-based implementation much easier to reason about.
import { createHook, ReactHook } from 'class-based-react-hooks';

// Takes a key and a fetcher function.
// You always need to provide a fetcher, unlike with SWR where `fetch` is used by default.
type Args<Data, Key> = [Key, (key: Key) => Promise<Data>];
type State<Data> = {
	data: Data | undefined;
	error: unknown | undefined;
};
type Returns<Data> = {
	data: Data | undefined;
	error: unknown | undefined;
	isValidating: boolean;
	mutate: (newData?: Data, shouldRevalidate?: boolean) => void;
};

class UseMiniSWR<Data, Key> extends ReactHook<
	Args<Data, Key>,
	State<Data>,
	Returns<Data>
> {
	constructor(args: Args<Data, Key>) {
		// Initial state: both data and error is undefined.
		super(args, {
			data: undefined,
			error: undefined,
		});
	}
	// A RequestId to keep track of the latest request (and avoid concurrency bugs).
	_lastRequestId: number = 1;
	// Whether or not the latest request is still pending.
	_isRequestActive: boolean = false;
	// This will be run when the key changes, or when mutate is called.
	_fetchData = async (key: Key) => {
		const requestId = ++this._lastRequestId;
		// _isRequestActive is a field (not inside this.state)
		// because _fetchData is called from hookWillMount, hookWillUpdate, and _mutate,
		// and all three of those guarantee that a render will follow anyway.
		this._isRequestActive = true;
		const update: Partial<State<Data>> = {};
		try {
			const data = await this.args[1](key);
			update.data = data;
			update.error = undefined;
		} catch (error) {
			update.error = error;
		}
		if (this._lastRequestId === requestId) {
			// Here, too, a render is scheduled (by setState) after _isRequestActive is changed.
			this._isRequestActive = false;
			this.setState(update);
		}
	};
	hookWillMount(): void {
		// Start fetching the data as soon a we can!
		this._fetchData(this.args[0]);
	}
	hookWillUpdate(nextArgs: Args<Data, Key>, nextState: State<Data>): void {
		if (nextArgs[0] !== this.args[0]) {
			this._fetchData(nextArgs[0]);
		}
	}
	_mutate = (newData?: Data, shouldRevalidate: boolean = true) => {
		const update: Partial<State<Data>> = {};
		if (newData) {
			update.data = newData;
		}
		if (shouldRevalidate) {
			this._fetchData(this.args[0]);
		}
		this.setState(update);
	};
	render(): Returns<Data> {
		return {
			data: this.state.data,
			error: this.state.error,
			isValidating: this._isRequestActive,
			mutate: this._mutate,
		};
	}
}
const useMiniSWR = createHook(UseMiniSWR);

async function fetcher(uri: string): Promise<string> {
	const resp = await fetch(uri);
	return await resp.text();
}
function MyComponent() {
	const {data, mutate, isValidating} = useMiniSWR("https://raw.githubusercontent.com/wishawa/class-based-react-hooks/main/README.md", fetcher);
	return (
		<div>
			{isValidating && <span>Loading...</span>}
			{!!data && <span>{data}</span>}
			<button onClick={() => mutate()}>refresh</button>
		</div>
	);
}

API

This library is typed with TypeScript. Usage with TypeScript is strongly recommended.

ReactHook

ReactHook tries to mimick React.Component's API where applicable. Below is the definition for ReactHook with documentation comments.

/// ReactHook is the class you have to extend to create a hook.
/// It has 3 generic arguments:
/// - `Args`: The arguments that the hook receives. Think of this as the props of `React.Component`.
///	Examples: `[number] -> useHook(42)`, `[string, typeof someObj] -> useHook("asdf", someObj)`.
/// - `State`: The state of the hook. This is like the state of a `React.Component`. It must be an object.
/// - `Return`: What this hook returns.
abstract class ReactHook<Args extends any[], State extends {}, Return> {
	/// The constructor takes the `Args` and an initial `State`.
	/// This is ❗different❗ from `React.Component`'s constructor, which takes only the props.
	/// So in your inherited class, instead of doing `super(args); this.state = {...};`,
	/// you should do `super(args, {...});`.
	constructor(args: Args, state: State) {
		// Omitted
	}

	/// You can access the hook's state through `this.state`.
	/// You should not try to modify it directly. Use `this.setState`.
	protected state: State;

	/// You can access the hook's args through `this.args`.
	protected args: Args;


	/// This method must be defined by the subclass. It must returns the `Return` type.
	/// Usually, this method would return things from `this.state` or `this.args`.
	/// This is called every time the component containing this hook renders.
	protected abstract render(): Return;

	/// Called just before the first `render()`.
	/// At this point `this.state` and `this.args` are already available.
	protected hookWillMount(): void {}

	/// Called after the first `render()` is all finished - the same time as `componentDidMount`.
	protected hookDidMount(): void; {}
	
	/// Called just before `render()`. Not called for the first render.
	/// This method is called even if the args and state remains the same so you must perform comparison yourself.
	/// At this point `this.args` and `this.state` are the args and state from the previous render -
	/// `nextArgs` and `nextState` are those of the upcoming render.
	protected hookWillUpdate(nextArgs: Args, nextState: State): void {}

	/// Called after `render()` is all finished - the same time as `componentDidUpdate`. Not called for the first render.
	/// This method is called even if the args and state remains the same so you must perform comparison yourself.
	protected hookDidUpdate(prevArgs: Args, prevState: State): void {}

	/// Called just before the component unmounts.
	protected hookWillUnmount(): void {}

	/// Called before every `render()`, after `hookWillMount` / `hookWillUpdate`.
	/// This should be a pure function.
	protected getDerivedStateFromArgs(
		this: {},
		nextArgs: Args,
		prevState: State
	): null | Partial<State> {
		return null;
	}


	/// Schedule the component containing this hook to rerender.
	/// A callback can be provided to be run after that render is finished.
	protected forceUpdate(callback?: Callback) {
		// Omitted
	}
	/// Update the state of the hook. Takes a partial state and merge it into the new state.
	/// State update is scheduled and not immediately reflected in `this.state`, just like in `React.Component`.
	/// A callback can be provided to be run after that update is rendered.
	protected setState(update: Partial<State>, callback?: Callback) {
		// Omitted
	}
}

createHook

The createHook function takes a class (the class itself, not an instance) that

  • Extends ReactHook<Args, State, Return>.
  • Has constructor signature constructor(args: Args).

It returns a hook function. The returned hook has signature (...args: Args) => Return.