React中Nivo Rocks图表Y轴刻度异常问题求助
解决Nivo ResponsiveBar Y轴刻度未适配数据最大值的问题
问题描述
在React项目中使用Nivo Rocks的ResponsiveBar图表时,出现Y轴刻度未正确适配数据最大值的问题——比如当数据中“Latest offer”字段值为893时,Y轴仅显示到800,未能包含完整数值。该问题仅在部分场景出现,需修复以满足QA要求。
代码实现
const ValueAtStack = () => { const { callData, data, loading } = useNegotiationGlanceStackValue(); const params = useParams<any>(); const { data: dataNegotiation } = useNegotiation(params.id); useEffect(() => { if (params.id) callData({ negotiationId: params.id }); }, [params.id]); const colors = [ ThemeColors().blueDark1, ...(data?.data ? data?.data?.map(() => ThemeColors().blue) : []), ]; return ( <Item icon={<DollarOutlined />} title={t("Saving")} loading={loading} empty={data?.data?.length === 0 || !data?.data} emptyDescription={t("Please add some negotiation rounds in Result Tracker")} emptyExtra={ <CustomButton href={`/pipelines/fact-sheet/${params.id}/lead-negotiation/results-tracker`}> {t("Results Tracker")} </CustomButton> }> <div className={classes.description}> <Currency replace={dataNegotiation?.currencyId} /> {t(`'${data?.unit}`)} </div> <div className={classes.content} style={{ height: 300, width: window.innerWidth - 300, }}> {data && ( <ResponsiveBar data={[data.target, ...data?.data]} keys={["Latest offer"]} indexBy="date" margin={{ top: 50, right: 20, bottom: 50, left: 60 }} padding={0.3} colors={colors} colorBy="indexValue" valueScale={{ type: "linear" }} indexScale={{ type: "band", round: true }} defs={[ { id: "dots", type: "patternDots", background: "inherit", color: "#38bcb2", size: 4, padding: 1, stagger: true, }, { id: "lines", type: "patternLines", background: "inherit", color: "#eed312", rotation: -45, lineWidth: 6, spacing: 10, }, ]} borderColor={{ from: "color", modifiers: [["darker", 1.6]], }} axisTop={null} axisRight={null} axisBottom={{ tickSize: 5, tickPadding: 5, tickRotation: data.length > 5 ? 45 : 0, legendPosition: "middle", legendOffset: 32, }} axisLeft={{ tickSize: 5, tickPadding: 5, tickRotation: 0, legendPosition: "middle", legendOffset: -40, }} labelSkipWidth={12} labelSkipHeight={12} labelTextColor={{ from: "color", modifiers: [["darker", 1.6]], }} role="application" /> )} </div> </Item> ); }; export default ValueAtStack;
最小测试数据
{ date: "2023-03-23T12:37:32.835", "Latest offer": 893, }
解决方案
问题根源在于Nivo默认的线性刻度计算会自动取整到最近的“友好”数值,导致最大值未被包含。以下两种方式可彻底解决:
方法1:手动指定Y轴刻度范围
通过配置valueScale的domain属性,强制设置Y轴的最小值和最大值。先计算数据中的最大值,再将最大值(或加少量余量)作为domain的上限,确保刻度覆盖完整数据:
// 在组件内部计算数据最大值 const maxValue = data ? Math.max( data.target["Latest offer"], ...data.data.map(item => item["Latest offer"]) ) : 0; // 修改ResponsiveBar的valueScale配置 valueScale={{ type: "linear", domain: [0, maxValue * 1.1] // 乘以1.1给图表顶部留余量,也可直接用maxValue }}
方法2:自定义Y轴刻度值
通过axisLeft的tickValues属性生成精确的刻度,确保最大值被包含在内:
// 计算数据最大值 const maxValue = data ? Math.max( data.target["Latest offer"], ...data.data.map(item => item["Latest offer"]) ) : 0; // 生成自定义刻度,按步长适配数据 const generateTicks = (max) => { const ticks = []; const step = 100; // 可根据数据量级调整步长 for (let i = 0; i <= max; i += step) { ticks.push(i); } // 如果最大值不是步长的倍数,手动添加最大值作为最后一个刻度 if (max % step !== 0) { ticks.push(max); } return ticks; }; // 修改ResponsiveBar的axisLeft配置 axisLeft={{ tickSize: 5, tickPadding: 5, tickRotation: 0, legendPosition: "middle", legendOffset: -40, tickValues: generateTicks(maxValue) }}
完整修改后的组件片段
const ValueAtStack = () => { const { callData, data, loading } = useNegotiationGlanceStackValue(); const params = useParams<any>(); const { data: dataNegotiation } = useNegotiation(params.id); useEffect(() => { if (params.id) callData({ negotiationId: params.id }); }, [params.id]); const colors = [ ThemeColors().blueDark1, ...(data?.data ? data?.data?.map(() => ThemeColors().blue) : []), ]; // 计算数据中的最大值 const maxValue = data ? Math.max( data.target["Latest offer"], ...data.data.map(item => item["Latest offer"]) ) : 0; return ( <Item icon={<DollarOutlined />} title={t("Saving")} loading={loading} empty={data?.data?.length === 0 || !data?.data} emptyDescription={t("Please add some negotiation rounds in Result Tracker")} emptyExtra={ <CustomButton href={`/pipelines/fact-sheet/${params.id}/lead-negotiation/results-tracker`}> {t("Results Tracker")} </CustomButton> }> <div className={classes.description}> <Currency replace={dataNegotiation?.currencyId} /> {t(`'${data?.unit}`)} </div> <div className={classes.content} style={{ height: 300, width: window.innerWidth - 300, }}> {data && ( <ResponsiveBar data={[data.target, ...data?.data]} keys={["Latest offer"]} indexBy="date" margin={{ top: 50, right: 20, bottom: 50, left: 60 }} padding={0.3} colors={colors} colorBy="indexValue" valueScale={{ type: "linear", domain: [0, maxValue * 1.1] }} indexScale={{ type: "band", round: true }} defs={[ { id: "dots", type: "patternDots", background: "inherit", color: "#38bcb2", size: 4, padding: 1, stagger: true, }, { id: "lines", type: "patternLines", background: "inherit", color: "#eed312", rotation: -45, lineWidth: 6, spacing: 10, }, ]} borderColor={{ from: "color", modifiers: [["darker", 1.6]], }} axisTop={null} axisRight={null} axisBottom={{ tickSize: 5, tickPadding: 5, tickRotation: data.length > 5 ? 45 : 0, legendPosition: "middle", legendOffset: 32, }} axisLeft={{ tickSize: 5, tickPadding: 5, tickRotation: 0, legendPosition: "middle", legendOffset: -40, }} labelSkipWidth={12} labelSkipHeight={12} labelTextColor={{ from: "color", modifiers: [["darker", 1.6]], }} role="application" /> )} </div> </Item> ); }; export default ValueAtStack;
内容的提问来源于stack exchange,提问作者ijavad
相关产品推荐
相关产品推荐

