Skip to content

useDerivedState

Hook useful when the internal state of a component depends on one or more props. It receives an initial state and a dependency array that works the same way as that of a useEffect, useMemo, and useCallback. Every time the dependencies change, the derived state is resetted to initial state. A third optional parameter can be passed, to execute a compute function after the dependencies are updated, without having a useEffect within the component.

Demo

INFO

The component has three internal string states and renders three input fields and three components that receive one state each. These three components have an object as internal state with two properties loading, initially set to true, and friends which is an initially empty array. Based on the user prop they receive, they set the loading property of the internal state to true and invoke a serverAPI function that simulates a backend call and returns a list of names filtered by the passed prop. This list values the friends property of the internal state and this list together with the passed user prop are rendered:

  • The Without useDerivedState component uses the useState and useEffect hooks to implement this logic.
  • The With useDerivedState component uses the useDerivedState hook and the useEffect.
  • The With useDerivedStateAndCompute component uses the useDerivedState hook and the optional third parameter to implement all logic.

Each component also renders a counter of the times it is rendered.

The component without useDerivedState hook is rendered one more time every time its prop changes while the other two have the same number of renders.

Furthermore, if you debug the code you can see how in the first component there is no synchronization in the updating of the values since in a first render the rendered prop user is updated and in a second render the writing loading is rendered instead of the list of names.

Loading demo…
Show source code
tsx
import { memo, useEffect, useState } from "react";
import { useDerivedState } from "../../../..";

const serverAPI = (user: string) => {
	const names = [
		"Keith Allison",
		"Ora Nelson",
		"Thomas Chavez",
		"Claudia Wheeler",
		"Iva Gibson",
		"Jose Daniel",
		"Mattie Greer",
		"Belle Schultz",
		"Milton Weber",
		"Olive Rivera",
		"Patrick Caldwell",
		"Adeline Wheeler",
		"Arthur Russell",
		"Anthony Hogan",
		"Rodney Munoz",
		"Ricky Woods",
		"Sean Stone",
		"Leona Leonard",
		"Brent Turner",
		"Cecelia Parks",
	]
	return new Promise(res => setTimeout(res, 2000))
		.then(() => {
			return names.filter(n => n.toLowerCase().includes(user.toLowerCase()));
		})
};

const renders = {
	1: 0,
	2: 0,
	3: 0
}

export const UseDerivedState = () => {
	const [state, setState] = useState("");
	const [state1, setState1] = useState("");
	const [state2, setState2] = useState("");

	return <div style={{ display: "grid", gridTemplateColumns: "auto auto auto", justifyContent: "center", gap: 50, maxHeight: 350, overflow: "auto" }}>
		<div>
			<p>Without useDerivedState</p>
			<input type="text" placeholder="User.." value={state1} onChange={(e) => setState1(e.target.value)} />
			<WithoutUseDerivedState user={state1} />
		</div>
		<div>
			<p>With useDerivedState</p>
			<input type="text" placeholder="User.." value={state} onChange={(e) => setState(e.target.value)} />
			<WithUseDerivedState user={state} />
		</div>
		<div>
			<p>With useDerivedState and compute</p>
			<input type="text" placeholder="User.." value={state2} onChange={(e) => setState2(e.target.value)} />
			<WithUseDerivedStateAndCompute user={state2} />
		</div>
	</div>
}

const WithoutUseDerivedState = memo(({user}:{user:string}) => {
	renders[1]++;
	const [state, setState] = useState<{ loading: boolean, friends: string[] }>({ loading: true, friends: [] });

	useEffect(() => {
		setState({ loading: true, friends: [] });
		serverAPI(user).then(data => setState({ loading: false, friends: data }));
	}, [user])

	return <>
		<h2>User: {user}</h2>
		<h3>Renders: {renders[1]}</h3>
		{
			state.loading
				? "Loading..."
				: <ul>
					{
						state.friends.map(f => <li key={f}>{f}</li>)
					}
				</ul>
		}
	</>
})

const WithUseDerivedStateAndCompute = memo(({ user }: { user: string }) => {
	renders[2]++;
	const [state, setState] = useDerivedState<{ loading: boolean, friends: string[] }>(
		{ loading: true, friends: [] },
		[user],
		() => {
			state.loading && serverAPI(user).then(data => {
				setState({ loading: false, friends: data })
			});
		}
	);

	return <>
		<h2>User: {user}</h2>
		<h3>Renders: {renders[2]}</h3>
		{
			state.loading
				? "Loading..."
				: <ul>
					{
						state.friends.map(f => <li key={f}>{f}</li>)
					}
				</ul>
		}
	</>
})

const WithUseDerivedState = memo(({ user }: { user: string }) => {
	renders[3]++;
	const [state, setState] = useDerivedState<{loading: boolean, friends: string[]}>(
		{ loading: true, friends: [] },
		[user]
	);

	useEffect(() => {
		serverAPI(user).then(data => {
			setState({ loading: false, friends: data })
		});
	}, [setState, user]);

	return <>
		<h2>User: {user}</h2>
		<h3>Renders: {renders[3]}</h3>
		{
			state.loading
				? "Loading..."
				: <ul>
					{
						state.friends.map(f => <li key={f}>{f}</li>)
					}
				</ul>
		}
	</>
})

Types

UseDerivedStateProps

  • @template T - The type of the state value.

Parameters accepted by useDerivedState.

PropertyTypeRequiredDescription
initialStateT \| (() => T)The initial state value. Accepts either a direct value or a lazy initializer function invoked only on the first render.
depsDependencyListA React dependency array (same semantics as useEffect). When any dependency changes, compute is invoked to derive and apply a new state value.
computeEffectCallbackAn optional effect callback invoked whenever deps change, following the same semantics as useEffect. Use it to derive the next state value and call the returned setter to update it. When omitted, the state is not automatically recomputed on dependency changes.

UseDerivedStateResult

  • @template T - The type of the state value.

Return value of useDerivedState.

IndexTypeDescription
[0]TThe current state value, reactive — triggers a re-render when updated.
[1]Dispatch<SetStateAction<T>>A stable setter function (same semantics as the setter returned by useState). Accepts either a new value or an updater function receiving the current state and returning the next state.

Released under the MIT License.