Bạn viết 1 cái <Toggle />.
User A muốn: <Toggle /> (Tự quản lý state bật tắt).
User B muốn: <Toggle on={isOn} onChange={setOn} /> (Component cha quản lý).
Làm sao đáp ứng cả 2?
1. Logic Hybrid
Chúng ta cần kiểm tra: Nếu user truyền prop value -> Dùng mode Controlled. Nếu không -> Dùng mode Uncontrolled (state nội bộ).
tsx:
function useControlled({ value, defaultValue, onChange }) {
const [internalValue, setInternalValue] = useState(defaultValue);
const isControlled = value !== undefined;
// Nếu controlled -> dùng prop. Nếu không -> dùng state nội bộ.
const currentValue = isControlled ? value : internalValue;
const setValue = (newValue) => {
// Luôn gọi callback ra ngoài
if (onChange) onChange(newValue);
// Chỉ update state nội bộ nếu là Uncontrolled
if (!isControlled) {
setInternalValue(newValue);
}
};
return [currentValue, setValue];
}2. Usage
tsx:
function Toggle({ on, defaultOn = false, onChange }) {
const [isOn, setIsOn] = useControlled({
value: on,
defaultValue: defaultOn,
onChange
});
return <button onClick={() => setIsOn(!isOn)}>{isOn ? 'ON' : 'OFF'}</button>;
}
// Case 1: Uncontrolled (Tự sướng)
<Toggle defaultOn={true} />
// Case 2: Controlled (Cha quản lý)
<Toggle on={appState} onChange={setAppState} />Kết luận
Đây là bí mật đằng sau sự linh hoạt của các thư viện lớn như MUI, Ant Design. Pattern này giúp component của bạn dễ tính hơn, ai dùng kiểu nào cũng chiều được.