FreeGridv1.1.0English

실시간 갱신

실시간으로 들어오는 데이터를 id로 갱신하고, 바뀐 셀을 깜빡여 보여 줍니다.

이 데모의 코드

import { createGrid, type Row } from '@freegridjs/core';
import { button, separator, text, toggle, tr, type DemoContext } from '../ui';

export default function mount(ctx: DemoContext) {
  const { lang } = ctx;
  const h = (en: string, ko: string) => tr(lang, { en, ko });
  const symbols = ['SAMSUNG', 'SK HYNIX', 'NAVER', 'KAKAO', 'HYUNDAI', 'LG CHEM', 'POSCO', 'KB FIN', 'CELLTRION', 'KIA', 'SHINHAN', 'COUPANG'];
  const data: Row[] = symbols.map((name, i) => {
    const price = 20000 + i * 13700;
    return { id: name, name, price, change: 0, volume: 1000 + i * 120, history: [price, price, price, price, price, price, price, price] };
  });
  const grid = createGrid(ctx.host, {
    columns: [
      { field: 'name', header: h('Symbol', '종목'), width: 140, pinned: 'left' },
      { field: 'price', header: h('Price', '현재가'), type: 'number', format: '#,##0', width: 120 },
      { field: 'change', header: h('Change', '등락률'), type: 'number', format: '0.00%', width: 110,
        cellClass: ({ value }) => (Number(value) > 0 ? 'demo-up' : Number(value) < 0 ? 'demo-down' : undefined) },
      { field: 'history', header: h('Last moves', '최근 흐름'), width: 140, sparkline: { type: 'area' }, sortable: false, filter: false },
      { field: 'volume', header: h('Volume', '거래량'), type: 'number', format: '#,##0', width: 120 },
    ],
    data, locale: lang, getRowId: r => String(r.id), rowNumbers: true,
  });

  let timer = 0;
  const tick = () => {
    // a pretend market feed: a few symbols move each tick
    const updates = data.filter(() => Math.random() < 0.35).map(r => {
      const price = Math.max(1000, Math.round(r.price * (1 + (Math.random() - 0.5) * 0.02)));
      const base = r.history[0] as number;
      return { id: r.id, price, change: (price - base) / base, volume: (r.volume as number) + Math.round(Math.random() * 50), history: [...(r.history as number[]).slice(1), price] };
    });
    grid.applyTransactionAsync({ update: updates });
  };
  grid.on('transactionApplied', e => ctx.log(`transactionApplied → ${h('updated', '갱신')} ${e.updated.length}`));

  text(ctx, h('Prices arrive like a websocket feed. Only the changed rows are updated, and changed cells flash. Sorting by Change keeps up as prices move.',
    '웹소켓처럼 시세가 들어옵니다. 바뀐 행만 갱신되고 바뀐 셀이 깜빡입니다. 등락률로 정렬해 두면 순위가 계속 바뀝니다.'));
  separator(ctx);
  const start = () => { timer = window.setInterval(tick, 700); };
  toggle(ctx, h('Live feed', '실시간 수신'), true, on => {
    clearInterval(timer);
    if (on) start();
  });
  button(ctx, h('Add a symbol', '종목 추가'), () => {
    const name = `NEW ${Math.floor(Math.random() * 900 + 100)}`;
    const row = { id: name, name, price: 10000, change: 0, volume: 0, history: Array(8).fill(10000) };
    data.push(row);
    grid.applyTransaction({ add: [row], addIndex: 0 });
  });
  button(ctx, h('Remove the last', '마지막 종목 삭제'), () => {
    const row = data.pop();
    if (row) grid.applyTransaction({ remove: [row] });
  });
  start();
  // stop when the page goes away
  window.addEventListener('pagehide', () => clearInterval(timer), { once: true });
  return grid;
}