Skip to content

Instantly share code, notes, and snippets.

@mdevils
Created July 7, 2020 11:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mdevils/b861610780300784292194f65886517a to your computer and use it in GitHub Desktop.
Save mdevils/b861610780300784292194f65886517a to your computer and use it in GitHub Desktop.
import React, {useCallback, useMemo, useState} from 'react';
/**
* Replacement for useState which depends on prop value and avoids unnecessary re-renders.
*/
export function useStateBasedOnProps(propValue) {
const [, update] = useState(false);
const [state, setState] = useMemo(() => {
const state = {};
return [
state,
(value) => {
if (value instanceof Function) {
state.value = value(state.value);
} else {
state.value = value;
}
update((tick) => !tick);
}
];
}, [update]);
if (state.prevPropValue !== propValue) {
state.prevPropValue = propValue;
state.value = propValue;
}
return [state.value, setState];
}
/**
* State of this component depends on prop value.
*/
function IncButton({initialValue}) {
const [value, setValue] = useStateBasedOnProps(initialValue);
const increment = useCallback(() => setValue((prev) => prev + 1), [setValue]);
return <button onClick={increment}>Click to increment: {value}</button>;
}
/**
* This is the demo component.
*/
export function Test() {
const [initialValue, setInitialValue] = useState(0);
const reduceBaseValue = useCallback(() => setInitialValue((value) => value - 1), [setInitialValue]);
return (
<div>
<button onClick={reduceBaseValue}>Reduce initial value</button>
<IncButton initialValue={initialValue} />
</div>
);
}
import React, {Dispatch, SetStateAction, useCallback, useMemo, useState} from 'react';
/**
* Replacement for useState which depends on prop value and avoids unnecessary re-renders.
*/
export function useStateBasedOnProps<T>(propValue: T): [T, Dispatch<SetStateAction<T>>] {
const [, update] = useState(false);
const [state, setState] = useMemo(() => {
const state = {} as {value?: T; prevPropValue?: T};
return [
state,
(value: SetStateAction<T>) => {
if (value instanceof Function) {
state.value = value(state.value!);
} else {
state.value = value;
}
update((tick) => !tick);
}
];
}, [update]);
if (state.prevPropValue !== propValue) {
state.prevPropValue = propValue;
state.value = propValue;
}
return [state.value!, setState];
}
/**
* State of this component depends on prop value.
*/
function IncButton({initialValue}: {initialValue: number}) {
const [value, setValue] = useStateBasedOnProps(initialValue);
const increment = useCallback(() => setValue((prev) => prev + 1), [setValue]);
return <button onClick={increment}>Click to increment: {value}</button>;
}
/**
* This is the demo component.
*/
export function Test() {
const [initialValue, setInitialValue] = useState(0);
const reduceBaseValue = useCallback(() => setInitialValue((value) => value - 1), [setInitialValue]);
return (
<div>
<button onClick={reduceBaseValue}>Reduce initial value</button>
<IncButton initialValue={initialValue} />
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment