经纬志
首页 / 实战手册 / deck.gl 可视化

setProps 改了数据,图层却纹丝不动

先看结论

点了筛选按钮,控制台里的数组确实变了,地图上的点一个没少,颜色也没变。

最后验证 2026-11-03 · 多技术栈 · 更新 2026-10-29 · 引用本文

现象

每次筛选都调用 deck.setProps({ layers: [newLayer] }),但画面没有变化。 只有手动缩放地图触发重绘时,偶尔会闪一下新的结果。 没有报错,deck.layerManager.getLayers() 里的图层对象也是新的。

原因

deck.gl 用图层 id 加属性做浅比较来决定是否更新。 以下几种写法会让它认为“没有变化”:

写法结果
同一个数组 push/splice 后原样传入引用未变,跳过更新
同一个图层实例反复传入属性未变,跳过更新
数据变了但只改了 getColor 依赖的字段访问器结果未重算,颜色不变
在 deck.setProps 之外直接改 layer.props.data不触发 diff

排查过程

  1. 打印 deck.layerManager.getLayers().map(l => l.id),确认图层确实被替换。
  2. 打印 deck.props.layers[0].props.data.length,看数据长度是否变化。
  3. 给 ScatterplotLayer 加一个 onDataLoad 回调,确认数据是否被重新读取。

解决方案

每次都传新数组与新图层实例。最稳妥的做法是把数据转换放进 useMemo 或缓存函数里,避免每帧重建大数组:

function buildLayer(list) {
  return new deck.ScatterplotLayer({
    id: 'stations',
    data: list,                       // 必须是新数组
    getPosition: function (d) { return [d.lng, d.lat]; },
    getRadius: 8,
    radiusUnits: 'pixels',
    getFillColor: function (d) {
      return d.status === 'alarm' ? [220, 53, 69] : [23, 162, 184];
    },
    pickable: true
  });
}

function render(list) {
  deck.setProps({ layers: [buildLayer(list.slice())] });
}

只改访问器读取的字段时,用 updateTriggers 告诉 deck.gl 重新计算。 适合数据对象不变、但属性被就地更新的场景:

new deck.ScatterplotLayer({
  id: 'stations',
  data: stations,
  getPosition: function (d) { return [d.lng, d.lat]; },
  getFillColor: function (d) { return d.color; },
  updateTriggers: {
    getFillColor: [filterVersion]      // 版本号变化即重算
  }
});

使用 React 时,把 layers 作为 DeckGL 的 prop 传入, 不要在 effect 里手动调 setProps,两套更新机制会互相覆盖:

const layers = React.useMemo(function () {
  return [buildLayer(filteredData)];
}, [filteredData]);

return <DeckGL layers={layers} viewState={viewState} controller />;

验证

筛选后点数立即变化,不需要拖动地图才刷新。 在 onAfterRender 里读 layer.props.data.length, 与实际筛选结果一致。连续切换筛选条件三次,每次都正确。

别在渲染里改数据:在 getColor 之类的访问器里修改 d 的字段,会导致同一帧内多次调用结果不一致, 画面出现部分更新。数据转换应在图层创建之前完成。

引用本文

经纬志(2026)。《setProps 改了数据,图层却纹丝不动》。https://www.jingweizhi.com/cookbook/deckgl-layer-not-refresh.html(访问日期 2026-11-03)。