---
name: "useStructuredDevicesProps"
mode: "api"
versionRequirements:
  - { name: "@ray-js/panel-sdk", version: "1.16.0" }
title: "useStructuredDevicesProps"
---

## useStructuredDevicesProps

> [VERSION] @ray-js/panel-sdk >= 1.16.0

> 💡 Before use, both of the following must be met:
> 1. Mount SdmDevicesProvider and configure the associated device mapping
> 2. Each device has configured the dp-kit interceptor (createDpKit) during initialization
> Performance optimization (best practices):
> 1. Strongly recommend always scoping the selector to the exact feature point, e.g., useStructuredDevicesProps(props => props.main?.colour_data), to avoid full subscriptions that cause unnecessary rerenders when unrelated devices or feature points change.
> 2. If the selector must return an object containing multiple fields, the default shallow comparison (shallow equal) ensures rerender will not be triggered as long as the extracted values do not change. If needed, pass a custom equalityFn for fine-grained control.

### Description

Get the structured DP state of associated devices; requires the dp-kit interceptor

### Parameters

`Params`

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `selector` | `(structuredDevicesProps: { [key: string]: Record<string, any> }) => any;` | Yes | Selector function. Input: the collection of structured props for all associated devices. The key is the device identifier, and the value is the device’s structured DP data parsed by dp-kit |
| `equalityFn` | `(prev: any, next: any) => boolean;` | No | Custom comparison function; if it returns true, re-render is not triggered; default is shallow equal |

### Return Value

Type: `any`

Structured DP value matching the selector

### Examples

#### Subscribe only to a specific state of a single device

```tsx
import { useStructuredDevicesProps } from '@ray-js/panel-sdk';

export default function MainDeviceColour() {
  // Precisely select the main device's colour_data; re-render only when it changes
  const colourData = useStructuredDevicesProps(
    props => props.main?.colour_data
  );
  return <Text>H: {colourData?.h}, S: {colourData?.s}, V: {colourData?.v}</Text>;
}
```

#### Configure dp-kit and use structured state (full integration flow)

```tsx
// ---- 1. Parser: Custom Transformer example (using colour_data as an example) ----
type TColorData = { hue: number; saturation: number; value: number };
class ColourTransformer implements Transformer<TColorData> {
  defaultValue = { hue: 10, saturation: 1000, value: 1000 };
  uuid = 'colour_data';
  parser(value: string): TColorData {
    if (value.length !== 12) return this.defaultValue;
    const step = generateDpStrStep(value);
    return { hue: step(4).value, saturation: step(4).value, value: step(4).value };
  }
  formatter(data: TColorData) {
    const { hue, saturation, value } = data;
    return `${decimalToHex(hue, 4)}${decimalToHex(saturation, 4)}${decimalToHex(value, 4)}`;
  }
}

// ---- 2. Configure dpKit and inject into SmartDevicesManager ----
import { createDpKit, SmartDevicesManager, useStructuredDevicesProps } from '@ray-js/panel-sdk';
import { lampSchemaMap } from '@/devices/schema';

const dpKit = createDpKit({
  protocols: {
    [lampSchemaMap.colour_data.code]: new ColourTransformer(),
  }
});
const deviceManager = new SmartDevicesManager();

// Inject dp-kit interceptors during batchAdd
deviceManager.batchAdd([
  { key: 'lamp1', deviceId: 'xxx_device_id_1', interceptors: dpKit.interceptors },
  { key: 'lamp2', deviceId: 'xxx_device_id_2', interceptors: dpKit.interceptors },
]);

// ---- 3. Page component consumes structured data ----
export default function ColourDataList() {
  // Recommended: select only the required state, e.g., p => p.lamp1?.colour_data
  const structured = useStructuredDevicesProps(p => p);

  return (
    <View>
      {Object.keys(structured ?? {}).map(key => (
        <Text key={key}>
          {key} colour_data: hue={structured[key]?.colour_data?.hue}
        </Text>
      ))}
    </View>
  );
}
```

#### Custom rerender

```tsx
// useStructuredDevicesProps has built-in shallow equality checks on the selector's return value; in typical cases, you don't need to pass equalityFn.
import { useStructuredDevicesProps } from '@ray-js/panel-sdk';

export default function WorkModeMap() {
  const workModeMap = useStructuredDevicesProps(
    p => {
      const map: Record<string, string> = {};
      Object.keys(p ?? {}).forEach(k => { map[k] = p[k]?.work_mode; });
      return map;
    },
    (prev, next) => JSON.stringify(prev) === JSON.stringify(next)
  );

  return (
    <View>
      {Object.keys(workModeMap).map(k => (
        <Text key={k}>{k}: {workModeMap[k]}</Text>
      ))}
    </View>
  );
}
```
