# 受控与非受控

> React 颜色选择器的受控与非受控用法：何时传 value 或 defaultValue，其他同类 prop，以及两种变更回调的区别。

Source: https://chroma-panel.jscrate.dev/zh/react/handbook/controlled
Last updated: 2026-09-21

受控的 React 颜色选择器从你的状态中读取颜色值，非受控的选择器则自己保存颜色值。当前模式、弹出层的打开状态、尺寸和最近使用的颜色也都采用同样的用法。

## 非受控

这是默认方式。传入 `defaultValue`，由面板自己保存颜色值：

```tsx
<ColorInput defaultValue="#3366cc" onChangeComplete={(c) => save(c.hex)} />
```

## 受控

颜色值由你掌管。传入 `value`，并自己负责更新：

```tsx
const [color, setColor] = useState("#3366cc");

<ColorInput value={color} onChange={(c) => setColor(c.hex)} />;
```

> 传了 `value` 就必须处理 `onChange`，否则面板不会有任何变化。

在表单中，受控的 `ColorInput` 需要你自己重置，因为 `form.reset()` 改不了由你掌管的状态。详见[表单](https://chroma-panel.jscrate.dev/zh/react/handbook/forms)。

## 其他受控 prop

`mode`、`open`、`collapsed`、`size` 和 `recentColors` 都采用同样的用法：传入 prop 就由你控制，不传则交给面板自己管理。

| 受控           | 非受控                | 变更事件                       |
| -------------- | --------------------- | ------------------------------ |
| `value`        | `defaultValue`        | `onChange`, `onChangeComplete` |
| `mode`         | `defaultMode`         | `onModeChange`                 |
| `open`         | `defaultOpen`         | `onOpenChange`                 |
| `collapsed`    | `defaultCollapsed`    | `onCollapsedChange`            |
| `size`         | `defaultSize`         | `onSizeChange`                 |
| `recentColors` | `defaultRecentColors` | `onRecentColorsChange`         |

`open` 只存在于 [ColorInput](https://chroma-panel.jscrate.dev/zh/react/components/color-input) 上，其他 prop 在 [ChromaPanel](https://chroma-panel.jscrate.dev/zh/react/components/chroma-panel) 上同样可用。如果想让多个面板使用同一个颜色，改为给它们传入同一个 `store` 即可，详见[组合](https://chroma-panel.jscrate.dev/zh/react/handbook/composition)。

## onChange 与 onChangeComplete

拖动时，`onChange` 每秒大约触发 60 次；`onChangeComplete` 只在松开时触发一次。

实时预览用 `onChange`；有开销的操作用 `onChangeComplete`，比如保存、记录撤销步骤、发起网络请求。

> **不要把 onChange 存进全局状态**
>
> 拖动的每一帧都会触发它。把这个状态放在组件本地，或者改为在 `onChangeComplete`
> 中保存。

这也是[常见错误](https://chroma-panel.jscrate.dev/zh/react/handbook/common-mistakes)里的第一条。

## 交互元数据

如果你需要知道变更的来源，就使用 `onValueChange` 和 `onValueCommit`。它们同样分为实时触发和提交时触发，并通过第二个参数说明所处阶段和来源。

```tsx
<ColorInput
  onValueChange={(color, meta) => preview(color.css, meta.source)}
  onValueCommit={(color, meta) => save(color.css, meta.source)}
/>
```

这样一来，无需检查浏览器事件，就能对键盘编辑、图片取色、点选色块和代码触发的更新分别做不同处理。
