You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Polaris IndexTable动态数据更新问题求助

问题:Polaris IndexTable无法动态更新内容

我是Polaris/Shopify新手,使用<IndexTable />组件创建店铺优惠列表,已配置promotedBulkActions,API调用正常,但无法实现内容动态更新——删除条目后对应行未消失,切换状态后状态未自动变更。数据来自自定义API接口,解析后存入rowMarkup变量,知道需结合useState实现,但不清楚最后一步操作。

当前代码(已移除无关内容):

import {
    Card,
    IndexTable,
    useIndexResourceState,
    Filters,
    TextField
} from "@shopify/polaris";

import { useAppQuery, useAuthenticatedFetch } from "../hooks";
import { useState, useCallback } from 'react';
import { useEffect, useLayoutEffect } from "react";
import { TitleBar, useNavigate } from "@shopify/app-bridge-react";
import { data } from "@shopify/app-bridge/actions/Modal";



export function OfferTabs() {
    const [offers, setOffers] = useState([]);
    const [taggedWith, setTaggedWith] = useState('current');
    const [offerList, setOfferList] = useState([]);
    const [selected, setSelected] = useState(0);
    const [active, setActive] = useState(false);
    const [statusText, setStatusText] = useState('Active');
    const [status, setStatus] = useState(1);
    const navigate = useNavigate();


    useLayoutEffect(() => {
        fetch("xxxxx")
            .then(res => res.json())
            .then(result => {
               
                setOffers(result);
                
            });
    }, []);

    // const fetch = useAuthenticatedFetch();





    const getStatusText = (status) => {
        const _t = (status === 1) ? 'Active' : 'Inactive';
        //  setStatus(_t);
        return _t;
    }

    const getContent = (type) => {
        let _return = [];
        Object.keys(offers).map(o => {
            offers[o].map(m => {
                _return.push(
                    {
                        id: m.id,
                        name: m.name,
                        type: m.type,
                        start_date: m.start_date,
                        end_date: m.end_date,
                        status: m.status,
                        status_text: getStatusText(m.status),
                        shopify_id: m.shopify_id,
                        offer_type: o
                    }
                );
            });
        });
        
        return _return;
    }

    /* handling functions */

    const handleDelete = () => {

        const _itemCount = selectedResources.length;
        const _qtyStr = (_itemCount === 1) ? "this item" : `these ${_itemCount} items`
        if (confirm(`Do you want to delete ${_qtyStr}?
This cannot be undone!`)) {
             const _queryData = selectedResources.join(",");
             const _queryURL = `xxxx`;
             const _headers = {'method':'DELETE'}

            /* send the request and remove the elements from the DOM */

            fetch(_queryURL,_headers)
            .then(()=> {
             console.log("TO DO: Auto-refresh the content");
            });

        
            }



    }


    const handleStatusChange = (v) => {

        const _itemCount = selectedResources.length;
        const _qtyStr = (_itemCount === 1) ? "this item" : `these ${_itemCount} items`
        if (confirm(`Do you want to change the status of ${_qtyStr}?`)) {
            const _queryData = selectedResources.join(",");
            const _queryURL = `https://xxxxx`;
            const _headers = { 'method': 'PUT' }
             fetch(_queryURL,_headers)
             .then(()=> {
                console.log("TO DO: Auto-refresh the content");
            });
        }
    }


    /* this will convert the offers data into the correct format for an IndexTable */

    const indexContent = getContent();
    const resourceName = {
        singular: 'promotion',
        plural: 'promotions',
    };

    const { selectedResources, allResourcesSelected, handleSelectionChange } =
        useIndexResourceState(indexContent);

    const rowMarkup = indexContent.map(
        ({ id, name, type, start_date, end_date, status, status_text, offer_type }, index) => (
            <IndexTable.Row
                id={id}
                key={id}
                selected={selectedResources.includes(id)}
                position={index}
            >
                <IndexTable.Cell>{name}</IndexTable.Cell>
                <IndexTable.Cell>{type}</IndexTable.Cell>
                <IndexTable.Cell>{start_date}</IndexTable.Cell>
                <IndexTable.Cell>{end_date}</IndexTable.Cell>
                <IndexTable.Cell>{offer_type}</IndexTable.Cell>
                <IndexTable.Cell>{status_text}</IndexTable.Cell>
            </IndexTable.Row>
        ),
    );


    const promotedBulkActions = [
        {
            title: "Change Status",
            actions: [
                {
                    content: 'Activate',
                    onAction: () => handleStatusChange(1)
                },
                {
                    content: 'Deactivate',
                    onAction: () => handleStatusChange(0)
                }
            ]
        },
        {
            title: "Other Actions",
            actions: [
                {
                    content: 'Delete',
                    onAction: handleDelete
                }
            ]
        }
    ];





    return (
        <Card>
            <IndexTable
                resourceName={resourceName}
                itemCount={indexContent.length}
                selectedItemsCount={
                    allResourcesSelected ? 'All' : selectedResources.length
                }
                onSelectionChange={handleSelectionChange}
                promotedBulkActions={promotedBulkActions}
                headings={[
                    { title: 'Name' },
                    { title: 'Type' },
                    { title: 'Start Date' },
                    { title: 'End Date' },
                    { title: 'Schedule' },
                    { title: 'Status' }
                ]}
            >
                {rowMarkup}
            </IndexTable>

        </Card>
    )

}

操作步骤

  • 选中某一行
  • 执行“Activate”操作

预期结果

选中的行状态更新

实际结果

API调用成功,但行内容未更新


解决方案

核心是更新React状态offers,因为rowMarkup依赖offers生成,状态变更会触发组件重新渲染,自动更新表格内容。具体修改如下:

1. 封装数据获取函数

把初始加载的请求封装成可复用函数,方便操作后重新拉取最新数据:

const fetch = useAuthenticatedFetch(); // 取消注释,使用Shopify认证请求

const fetchOffers = useCallback(() => {
  fetch("xxxxx")
    .then(res => res.json())
    .then(result => {
      setOffers(result);
    });
}, [fetch]);

// 初始加载调用
useLayoutEffect(() => {
  fetchOffers();
}, [fetchOffers]);

2. 修改删除操作,更新状态

删除请求成功后,调用fetchOffers重新拉取数据:

const handleDelete = () => {
  const _itemCount = selectedResources.length;
  const _qtyStr = (_itemCount === 1) ? "this item" : `these ${_itemCount} items`;
  if (confirm(`Do you want to delete ${_qtyStr}?\nThis cannot be undone!`)) {
    const _queryData = selectedResources.join(",");
    const _queryURL = `xxxx`;
    const _options = { method: 'DELETE' };

    fetch(_queryURL, _options)
      .then(() => {
        fetchOffers(); // 重新拉取最新数据,触发表格更新
      });
  }
}

3. 修改状态切换操作,更新状态

状态修改请求成功后同样重新拉取数据:

const handleStatusChange = (v) => {
  const _itemCount = selectedResources.length;
  const _qtyStr = (_itemCount === 1) ? "this item" : `these ${_itemCount} items`;
  if (confirm(`Do you want to change the status of ${_qtyStr}?`)) {
    const _queryData = selectedResources.join(",");
    const _queryURL = `https://xxxxx`;
    const _options = { method: 'PUT' };

    fetch(_queryURL, _options)
      .then(() => {
        fetchOffers(); // 重新拉取最新数据,触发表格更新
      });
  }
}

4. 确保选中状态同步

useIndexResourceState依赖indexContent,每次offers更新时会自动重新计算选中状态,无需额外修改,确保组件渲染时该逻辑能获取最新的indexContent即可。


内容的提问来源于stack exchange,提问作者Anthony Earles

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 22:01:18