状态
如何在组件中管理状态,以及合并受控和非受控状态。
构建在可控与非受控模式下都能工作的灵活组件,是专业组件的标志。
非受控状态
非受控状态是指组件在内部管理自己的状态。这是大多数组件的默认使用模式。
例如,这里有一个简单的 Stepper 组件,它在内部管理自己的状态:
import { useState } from 'react';
export const Stepper = () => {
const [value, setValue] = useState(0);
return (
<div>
<p>{value}</p>
<button onClick={() => setValue(value + 1)}>Increment</button>
</div>
);
};受控状态
受控状态是指组件的状态由父组件管理。我们不在内部跟踪状态,而是将此责任委托给父组件。
让我们将 Stepper 组件重构为由父组件控制:
type StepperProps = {
value: number;
setValue: (value: number) => void;
};
export const Stepper = ({ value, setValue }: StepperProps) => (
<div>
<p>{value}</p>
<button onClick={() => setValue(value + 1)}>Increment</button>
</div>
);合并状态
优秀的组件同时支持受控和非受控状态。这使组件能够在各种场景下使用并且易于定制。
Radix UI 维护一个用于合并可控和非受控状态的内部工具,名为 use-controllable-state。虽然不打算公开使用,但像 Kibo UI 这样的实现已经采用了该工具来构建它们自己的类 Radix 组件。
让我们安装这个 hook:
npm install @radix-ui/react-use-controllable-state这个轻量级的 hook 为你提供了 Radix UI 组件库内部使用的相同状态管理模式,确保你的组件在行为上与行业标准保持一致。
该 hook 接受三个主要参数,并返回一个包含当前值和设置函数的元组。让我们使用它来合并 Stepper 组件的受控与非受控状态:
import { useControllableState } from '@radix-ui/react-use-controllable-state';
type StepperProps = {
value: number;
defaultValue: number;
onValueChange: (value: number) => void;
};
export const Stepper = ({ value: controlledValue, defaultValue, onValueChange }: StepperProps) => {
const [value, setValue] = useControllableState({
prop: controlledValue, // The controlled value prop
defaultProp: defaultValue, // Default value for uncontrolled mode
onChange: onValueChange, // Called when value changes
});
return (
<div>
<p>{value}</p>
<button onClick={() => setValue(value + 1)}>Increment</button>
</div>
);
}