Skip to content

Instantly share code, notes, and snippets.

@ajitid
Last active September 14, 2021 05:18
Show Gist options
  • Save ajitid/d999054ec788885c2b407fa0dc2a82e3 to your computer and use it in GitHub Desktop.
Save ajitid/d999054ec788885c2b407fa0dc2a82e3 to your computer and use it in GitHub Desktop.
Control props — state management for controllable components
interface Props {
value?: string;
onChange?: (value: string) => void;
}
export function OutboundSelect<T>({
value: externalValue,
onChange: externalOnChange = noop,
}: Props) {
const [value, onChange, isInternalValueUsed] = useAutoControlled(
externalValue,
externalOnChange,
""
);
function handleChange(event: React.ChangeEvent<{ value: unknown }>) {
const value = event.target.value;
if (typeof value !== "string") return;
onChange(value);
// Passed `onChange` handler should be called irrespective of whether
// we are using internal state or not.
if (isInternalValueUsed) {
externalOnChange(value);
}
}
import { useRef, useState } from "react";
/**
* Replacement for `useState` to do two-way data binding in both controlled and
* in uncontrolled form.
*
* When should you use this hook? If React (or Material UI) throw an error in
* console about a component changing an input from uncontrolled to controlled.
*/
export const useAutoControlled = <T>(
value: T | undefined,
setValue: (value: T) => unknown,
initialValue: T
): [
value: T | undefined,
setValue: (value: T) => unknown,
isInternalValueUsed: boolean
] => {
/*
We don't want to switch from uncontrolled to controlled state
in the midst of using the input. So we decide upfront which one of
them to use by defining this ref.
*/
const shouldUseInternalValueRef = useRef(value === undefined);
const [internalValue, setInternalValue] = useState(initialValue);
return shouldUseInternalValueRef.current
? [internalValue, (value: T) => setInternalValue(value), true]
: [value, setValue, false];
};
@ajitid
Copy link
Author

ajitid commented Sep 14, 2021

Note that in usage.tsx, unlike React we aren't showing any warning when:

  • value is passed but onChange handler isn't, or only value is passed but is not marked readOnly using a prop
  • input changes from controlled to uncontrolled
  • input changes from uncontrolled to controlled

This needs to be handled using NODE_ENV var and warning package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment