Redux Saga请求完成后使用返回数据渲染visx词云出现"read-only"报错
问题根因
- @visx/wordcloud 底层依赖 d3-cloud 布局库,该库的布局计算逻辑会直接修改传入的words数组内的每一个对象,写入
x、y、size、rotate等布局属性。 - 你从Redux中获取的
aggregation.data内的对象是被冻结的只读对象:Redux 开发环境默认会冻结所有状态对象防止直接修改,如果你使用了Immer等不可变工具也会产生冻结对象。d3 尝试向只读对象写入属性时,就会抛出对应报错。 - 静态数据是普通可写对象,因此运行正常。你之前做的克隆操作仅处理了数组外层,没有克隆数组内部的每个词对象,原对象还是只读状态,因此报错没有解决。
解决方法
在将数据传给词云组件前,对数组内的每个词对象做一次浅克隆,生成全新的可写对象供d3修改即可,有两种实现位置可选:
方案1:调用词云组件时处理
在 AggregationShowComponent 中传入参数时做克隆:
<WordCloud // 对每个词对象做浅拷贝,生成全新可写对象 words={aggregation.data.map(item => ({ ...item }))} width={1024} height={600} spiral="archimedean" rotate="0" />
方案2:词云组件内部统一处理
在 WordCloud 组件内部对传入的参数做克隆,一劳永逸避免后续调用出问题:
export default function WordCloud({ words, width, height, rotate = 0, spiral = 'archimedean' }: WordCloudProps) { // 先克隆所有词对象,避免修改原只读数据 const clonedWords = words.map(item => ({ ...item })) const fontScale = scaleLog({ domain: [Math.min(...clonedWords.map((w) => w.value)), Math.max(...clonedWords.map((w) => w.value))], range: [10, 100], }); const fontSizeSetter = (datum: WordData) => fontScale(datum.value); return ( <> <Wordcloud words={clonedWords} width={width} height={height} fontSize={fontSizeSetter} font={'Impact'} padding={2} spiral={spiral} rotate={rotate} random={fixedValueGenerator} > {(cloudWords) => cloudWords.map((w, i) => ( <Text key={w.text} fill={colors[i % colors.length]} textAnchor={'middle'} transform={`translate(${w.x}, ${w.y}) rotate(${w.rotate})`} fontSize={w.size} fontFamily={w.font} > {w.text} </Text> )) } </Wordcloud> </> ); }
如果你的词对象还有嵌套属性,把上述的浅克隆替换为深克隆即可,比如使用浏览器原生的 structuredClone() 方法。
内容的提问来源于stack exchange,提问作者FMK
相关产品推荐
相关产品推荐

