---
title: Using setData Wisely
docType: default
---

# Managing Data Updates Wisely

## How It Works Under the Hood

The Ray framework runs on the miniapp dual-thread architecture. When a React component's state changes, the underlying flow is:

```
Component calls setState / useState setter
    ↓
React executes reconciliation (diff computation)
    ↓
Ray sends changed data to the view layer via setData
    ↓
View layer updates the DOM and re-renders
```

Every React state update ultimately triggers an underlying `setData` call. Optimizing how state is updated means optimizing `setData` usage.

## Optimization Principles

### 1. Only Store Render-Related Data in State

Data unrelated to rendering should not be placed in `state` or `useState`. Doing so triggers unnecessary component re-renders and underlying setData calls.

```tsx
// ❌ Avoid: non-render data in state
import React, { useState } from 'react';

export default function Home() {
  const [list, setList] = useState([]);
  const [timer, setTimer] = useState(null);       // non-render data
  const [cache, setCache] = useState({});          // non-render data
  const [requestId, setRequestId] = useState('');  // non-render data
}
```

```tsx
// ✅ Recommended: use useRef for non-render data
import React, { useState, useRef } from 'react';

export default function Home() {
  const [list, setList] = useState([]);
  const timerRef = useRef(null);       // does not trigger re-render
  const cacheRef = useRef({});         // does not trigger re-render
  const requestIdRef = useRef('');     // does not trigger re-render
}
```

### 2. Reduce Unnecessary Re-renders

State changes in a React component cause the component and its children to re-render. Avoid triggering unnecessary renders.

**Use `React.memo` to memoize child components:**

```tsx
// ❌ Avoid: child re-renders every time parent updates
function ListItem({ item }) {
  return <View><Text>{item.name}</Text></View>;
}
```

```tsx
// ✅ Recommended: use memo to prevent unnecessary re-renders
const ListItem = React.memo(function ListItem({ item }) {
  return <View><Text>{item.name}</Text></View>;
});
```

**Use `useMemo` to cache computed values:**

```tsx
// ❌ Avoid: recomputes on every render
export default function Home() {
  const [list, setList] = useState([]);
  const sortedList = list.sort((a, b) => a.name.localeCompare(b.name));

  return <View>...</View>;
}
```

```tsx
// ✅ Recommended: cache with useMemo
import React, { useState, useMemo } from 'react';

export default function Home() {
  const [list, setList] = useState([]);
  const sortedList = useMemo(
    () => [...list].sort((a, b) => a.name.localeCompare(b.name)),
    [list] // only recompute when list changes
  );

  return <View>...</View>;
}
```

### 3. Throttle High-Frequency State Updates

High-frequency state updates (e.g., from scroll or input events) cause frequent re-renders and setData calls.

```tsx
// ❌ Avoid: update state on every scroll event
export default function Home() {
  const [scrollTop, setScrollTop] = useState(0);

  const handleScroll = (e) => {
    setScrollTop(e.detail.scrollTop);
  };

  return <ScrollView onScroll={handleScroll}>...</ScrollView>;
}
```

```tsx
// ✅ Recommended: use throttle to limit update frequency
import React, { useState, useRef, useCallback } from 'react';

export default function Home() {
  const [scrollTop, setScrollTop] = useState(0);
  const throttleRef = useRef(null);

  const handleScroll = useCallback((e) => {
    if (throttleRef.current) return;
    throttleRef.current = setTimeout(() => {
      setScrollTop(e.detail.scrollTop);
      throttleRef.current = null;
    }, 100);
  }, []);

  return <ScrollView onScroll={handleScroll}>...</ScrollView>;
}
```

### 4. Batch Multiple State Updates

React 18+ automatically batches multiple state updates, but may not batch automatically in async callbacks:

```tsx
// React auto-batches synchronous updates
const handleClick = () => {
  setName('New name');    // does not render immediately
  setAge(25);             // does not render immediately
  setStatus('active');    // all three merged into one render
};
```

```tsx
// For closely related states, consider useReducer or merging into one state object
// ❌ Multiple separate related states
const [loading, setLoading] = useState(false);
const [data, setData] = useState(null);
const [error, setError] = useState(null);

// ✅ Merge into one state object
const [state, setState] = useState({
  loading: false,
  data: null,
  error: null,
});

const fetchData = async () => {
  setState(prev => ({ ...prev, loading: true }));
  try {
    const res = await api.getData();
    setState({ loading: false, data: res, error: null });
  } catch (err) {
    setState({ loading: false, data: null, error: err });
  }
};
```

### 5. Stop Updates in Background Pages

Stop state updates when the page is not visible to avoid wasting resources:

```tsx
import React, { useState, useEffect, useRef } from 'react';
import { usePageEvent } from '@ray-js/ray';

export default function Home() {
  const [data, setData] = useState(null);
  const isActiveRef = useRef(true);
  const timerRef = useRef(null);

  usePageEvent('onShow', () => {
    isActiveRef.current = true;
    startPolling();
  });

  usePageEvent('onHide', () => {
    isActiveRef.current = false;
    if (timerRef.current) {
      clearInterval(timerRef.current);
    }
  });

  const startPolling = () => {
    timerRef.current = setInterval(async () => {
      if (isActiveRef.current) {
        const res = await fetchLatestData();
        setData(res);
      }
    }, 3000);
  };

  useEffect(() => {
    startPolling();
    return () => {
      if (timerRef.current) {
        clearInterval(timerRef.current);
      }
    };
  }, []);

  return <View>...</View>;
}
```

## Checklist

| Check Item | Symptom | Optimization |
| ---------- | ------- | ------------ |
| Non-render data in state | Unnecessary re-renders | Use useRef to store it |
| Child components re-rendering unnecessarily | Wasted performance | Use React.memo |
| High-frequency state updates | Laggy interactions | Apply throttle/debounce |
| Repeated computation logic | Slow rendering | Cache with useMemo |
| Multiple related states scattered | Multiple renders | Merge into one state or useReducer |
| Background page state updates | Wasted resources | Stop updates when page is hidden |
