Categories
React

Add Charts into Our React App with Victory — Shared and Dynamic Events

The Victory lets us add charts and data visualization into our React app.

In this article, we’ll look at how to add charts into our React app with Victory.

VictorySharedEvents

We can add events shared between multiple charts with the VictorySharedEvents component.

For instance, we can write:

import React from "react";
import {
  VictoryBar,
  VictoryLabel,
  VictoryPie,
  VictorySharedEvents
} from "victory";

export default function App() {
  return (
    <svg viewBox="0 0 450 350">
      <VictorySharedEvents
        events={[
          {
            childName: ["pie", "bar"],
            target: "data",
            eventHandlers: {
              onMouseOver: () => {
                return [
                  {
                    childName: ["pie", "bar"],
                    mutation: (props) => {
                      return {
                        style: Object.assign({}, props.style, {
                          fill: "tomato"
                        })
                      };
                    }
                  }
                ];
              },
              onMouseOut: () => {
                return [
                  {
                    childName: ["pie", "bar"],
                    mutation: () => {
                      return null;
                    }
                  }
                ];
              }
            }
          }
        ]}
      >
        <g transform={"translate(150, 50)"}>
          <VictoryBar
            name="bar"
            width={300}
            standalone={false}
            style={{
              data: { width: 20 },
              labels: { fontSize: 25 }
            }}
            data={[
              { x: "a", y: 2 },
              { x: "b", y: 3 },
              { x: "c", y: 5 }
            ]}
            labels={["a", "b", "c"]}
            labelComponent={<VictoryLabel y={290} />}
          />
        </g>
        <g transform={"translate(0, -75)"}>
          <VictoryPie
            name="pie"
            width={250}
            standalone={false}
            style={{ labels: { fontSize: 25, padding: 10 } }}
            data={[
              { x: "a", y: 1 },
              { x: "b", y: 4 },
              { x: "c", y: 5 }
            ]}
          />
        </g>
      </VictorySharedEvents>
    </svg>
  );
}

In the events prop, we add an array with the eventsHandlers object to add event handlers for various events.

We have event handlers for mouseover with onMouseOver and one for mouseout with onMouseOut .

The mutation method lets us change the fill style of the segment we hover over.

childName has the name values of the charts we want to change.

Similarly, we have the onMouseOut event handler to return null to revert to the original style.

External Event Mutations

We can attach event mutations by changing states.

For instance, we can write:

import React, { useState } from "react";
import { VictoryBar, VictoryChart } from "victory";

const buttonStyle = {
  backgroundColor: "black",
  color: "white",
  padding: "10px",
  marginTop: "10px"
};

export default function App() {
  const [externalMutations, setExternalMutations] = useState({});
  const removeMutation = () => {
    setExternalMutations(undefined);
  };

const clearClicks = () => {
    setExternalMutations([
      {
        childName: "Bar-1",
        target: ["data"],
        eventKey: "all",
        mutation: () => ({ style: undefined }),
        callback: removeMutation
      }
    ]);
  };

return (
    <div>
      <button onClick={clearClicks} style={buttonStyle}>
        Reset
      </button>
      <VictoryChart
        domain={{ x: [0, 5] }}
        externalEventMutations={externalMutations}
        events={[
          {
            target: "data",
            childName: "Bar-1",
            eventHandlers: {
              onClick: () => ({
                target: "data",
                mutation: () => ({ style: { fill: "orange" } })
              })
            }
          }
        ]}
      >
        <VictoryBar
          name="Bar-1"
          style={{ data: { fill: "grey" } }}
          labels={() => "click me!"}
          data={[
            { x: 1, y: 2 },
            { x: 2, y: 4 },
            { x: 3, y: 1 },
            { x: 4, y: 5 }
          ]}
        />
      </VictoryChart>
    </div>
  );
}

to add click event handlers with the events prop on VictoryChart .

When we click on the bars, the fill changes to orange.

The Reset button calls clearClicks to clear all the click handlers by setting externalMutations to undefined .

clearClicks clear all the styles from the bars.

And we set externalEventMutations to externalMutations .

Conclusion

We can add event handlers dynamically and add shared events into charts with React Victory.

Categories
React

Add Charts into Our React App with Victory — Plot Functions and Events

The Victory lets us add charts and data visualization into our React app.

In this article, we’ll look at how to add charts into our React app with Victory.

Plot Functions

We can plot functions in our React app with Victory easily.

For example, we can write:

import React from "react";
import { VictoryChart, VictoryLine } from "victory";

export default function App() {
  return (
    <VictoryChart>
      <VictoryLine
        samples={50}
        style={{ data: { stroke: "red", strokeWidth: 4 } }}
        y={(data) => Math.sin(2 * Math.PI * data.x)}
      />
    </VictoryChart>
  );
}

We set the samples prop to set the number of points to plot.

Then y is set to a function with the values we want to plot.

We get the x-axis value with data.x and do what we want with it.

Component Events

We can add event handlers for components in our charts.

For example, we can write:

import React from "react";
import { VictoryBar } from "victory";

export default function App() {
  return (
    <VictoryBar
      data={[
        { x: 1, y: 2, label: "A" },
        { x: 2, y: 4, label: "B" },
        { x: 3, y: 7, label: "C" },
        { x: 4, y: 3, label: "D" },
        { x: 5, y: 5, label: "E" }
      ]}
      events={[
        {
          target: "data",
          eventHandlers: {
            onClick: () => {
              return [
                {
                  target: "labels",
                  mutation: (props) => {
                    return props.text === "clicked"
                      ? null
                      : { text: "clicked" };
                  }
                }
              ];
            }
          }
        }
      ]}
    />
  );
}

to add the events prop into our VictoryBar component.

In it, we add the eventHandlers.onClick function to add click handlers to each bar.

Then in the mutation method, we toggle the ‘clicked’ text as we click the bars.

Nested Component Events

We can trigger change in one part of the chart from other parts of the chart.

For instance, we can write:

import React from "react";
import { VictoryArea, VictoryBar, VictoryChart, VictoryStack } from "victory";

export default function App() {
  return (
    <VictoryChart
      events={[
        {
          childName: ["area-1", "area-2"],
          target: "data",
          eventHandlers: {
            onClick: () => {
              return [
                {
                  childName: "area-4",
                  mutation: (props) => {
                    const fill = props.style.fill;
                    return fill === "green"
                      ? null
                      : { style: { fill: "green" } };
                  }
                }
              ];
            }
          }
        }
      ]}
    >
      <VictoryStack>
        <VictoryArea
          name="area-1"
          data={[
            { x: "a", y: 2 },
            { x: "b", y: 3 },
            { x: "c", y: 5 },
            { x: "d", y: 4 }
          ]}
        />
        <VictoryArea
          name="area-2"
          data={[
            { x: "a", y: 1 },
            { x: "b", y: 4 },
            { x: "c", y: 5 },
            { x: "d", y: 7 }
          ]}
        />
        <VictoryArea
          name="area-3"
          data={[
            { x: "a", y: 3 },
            { x: "b", y: 2 },
            { x: "c", y: 6 },
            { x: "d", y: 2 }
          ]}
        />
        <VictoryArea
          name="area-4"
          data={[
            { x: "a", y: 2 },
            { x: "b", y: 3 },
            { x: "c", y: 3 },
            { x: "d", y: 4 }
          ]}
        />
      </VictoryStack>
    </VictoryChart>
  );
}

We have 4 VictoryArea components to display filled areas formed from the points.

Then we add the events prop to VictoryChart .

childName has the name values of the components that triggers the mutation function.

Then in the onClick method we return an array with objects that specifies which component changes and how they change.

childName has the name of the component that changes.

mutation has the code to change the component specified in childName in the array returned by onClick .

We toggle the fill between 'green' and null on the VictoryArea with name area-4 .

Conclusion

We can plot functions and listen for events in chart components in React Victory’s charts.

Categories
React

Add Charts into Our React App with Victory — Data Processing

The Victory lets us add charts and data visualization into our React app.

In this article, we’ll look at how to add charts into our React app with Victory.

Specifying x and y Data

We can specify the data for the x and y axes with the property name if we have an array of objects as data.

For instance, we can write:

import React from "react";
import { VictoryBar, VictoryChart } from "victory";

export default function App() {
  return (
    <VictoryChart domainPadding={50}>
      <VictoryBar
        data={[
          { employee: "Jane Doe", salary: 65000 },
          { employee: "John Doe", salary: 62000 }
        ]}
        x="employee"
        y="salary"
      />
    </VictoryChart>
  );
}

We set the x and y props to the property names with the data for the x and y axes respectively.

If we have an array of arrays, we can specify the index with the data we want to display for the x and y axes:

import React from "react";
import { VictoryBar, VictoryChart } from "victory";

export default function App() {
  return (
    <VictoryChart domainPadding={50}>
      <VictoryBar
        data={[
          [1, 1],
          [2, 3],
          [3, 1]
        ]}
        x={0}
        y={1}
      />
    </VictoryChart>
  );
}

If we have an array od nested objects, we can also specify the path string or an array of strings to form the property path with the data we want to display:

import React from "react";
import { VictoryBar, VictoryChart } from "victory";

export default function App() {
  return (
    <VictoryChart domainPadding={50}>
      <VictoryBar
        data={[
          {
            employee: { firstName: "Jane", lastName: "Doe" },
            salary: { base: 65000, bonus: 2000 }
          },
          {
            employee: { firstName: "John", lastName: "Doe" },
            salary: { base: 62000, bonus: 6000 }
          }
        ]}
        x="employee.firstName"
        y={["salary", "base"]}
      />
    </VictoryChart>
  );
}

Processing Data

We can process data by passing in functions for the x and y props:

import React from "react";
import { VictoryAxis, VictoryBar, VictoryChart } from "victory";

export default function App() {
  return (
    <VictoryChart domainPadding={{ x: 40 }}>
      <VictoryBar
        data={[
          { experiment: "trial 1", expected: 3.95, actual: 3.21 },
          { experiment: "trial 2", expected: 3.95, actual: 3.38 },
          { experiment: "trial 3", expected: 3.95, actual: 2.05 },
          { experiment: "trial 4", expected: 3.95, actual: 3.71 }
        ]}
        x="experiment"
        y={(d) => (d.actual / d.expected) * 100}
      />
      <VictoryAxis
        label="experiment"
        style={{
          axisLabel: { padding: 30 }
        }}
      />
      <VictoryAxis
        dependentAxis
        label="percent yield"
        style={{
          axisLabel: { padding: 40 }
        }}
      />
    </VictoryChart>
  );
}

We pass in a function to compute the value of y to display with a function.

d has the data entry we want to compute.

Sorting Data

We can sort our data with the sortKey prop:

import React from "react";
import { VictoryChart, VictoryLine } from "victory";
import { range } from "lodash";

export default function App() {
  return (
    <VictoryChart domainPadding={{ x: 40 }}>
      <VictoryLine
        data={range(0, 2 * Math.PI, 0.01).map((t) => ({ t }))}
        sortKey="t"
        x={({ t }) => Math.sin(3 * t + 2 * Math.PI)}
        y={({ t }) => Math.sin(2 * t)}
      />
    </VictoryChart>
  );
}

Since sortKey is set to t , we sort by the t value.

Conclusion

We can process our data in various ways to create the chart we want with React Victory.

Categories
React

Add Charts into Our React App with Victory — Custom Chart Components

The Victory lets us add charts and data visualization into our React app.

In this article, we’ll look at how to add charts into our React app with Victory.

Polygon Charts

We can add polygon charts with the Polygon component.

For instance, we can write:

import React from "react";
import { VictoryChart, VictoryScatter } from "victory";

const data = [
  { x: 2, y: 1 },
  { x: 3, y: 5 },
  { x: 6, y: 3 }
];

const Polygon = (props) => {
  const getPoints = (data, scale) => {
    return data.reduce(
      (pointStr, { x, y }) => `${pointStr} ${scale.x(x)},${scale.y(y)}`,
      ""
    );
  };

  const { data, style, scale } = props;
  const points = getPoints(data, scale);
  return <polygon points={points} style={style} />;
};

export default function App() {
  return (
    <VictoryChart height={400} width={400} domain={[-10, 10]}>
      <Polygon data={data} style={{ fill: "tomato", opacity: 0.5 }} />
      <VictoryScatter data={data} />
    </VictoryChart>
  );
}

We create the getPoint function in the Polygon component to create the point string for the polygon path.

Then we pass that into the polygon element to create the points.

In App , we pass in the Polygon component with the data prop to create the polygon from the given corner points.

We render the same points as dots with the VictoryScatter component.

Using Victory Components to Create Custom Components

We can use Victory components to create custom components.

For instance, we can write:

import React from "react";
import {
  VictoryAxis,
  VictoryChart,
  VictoryGroup,
  VictoryLine,
  VictoryPie,
  VictoryScatter
} from "victory";

const data = [
  { x: 2, y: 1 },
  { x: 3, y: 5 },
  { x: 6, y: 3 }
];

const CustomPie = (props) => {
  const { datum, x, y } = props;
  const pieWidth = 120;

  return (
    <g transform={`translate(${x - pieWidth / 2}, ${y - pieWidth / 2})`}>
      <VictoryPie
        standalone={false}
        height={pieWidth}
        width={pieWidth}
        data={datum.pie}
        style={{ labels: { fontSize: 0 } }}
        colorScale={["#f77", "#55e", "#8af"]}
      />
    </g>
  );
};

export default function App() {
  return (
    <VictoryChart domain={{ y: [0, 100] }}>
      <VictoryAxis />
      <VictoryGroup data={data}>
        <VictoryLine />
        <VictoryScatter dataComponent={<CustomPie />} />
      </VictoryGroup>
    </VictoryChart>
  );
}

We create the CustomPie component that renders small pie charts with the VictoryPie component to render pie charts as points.

The in App , we render VictoryGroup to render the data by setting it as the data prop.

VictoryScatter lets us render the points.

And we set CustomPie as the dataComponent prop to render the pies as points.

We can pass in any SVG component as point components in the dataComponent prop.

For instance, we can use styled-components to create the SVG circles for the points:

import React from "react";
import styled from "styled-components";
import { VictoryChart, VictoryLine, VictoryScatter } from "victory";

const data = [
  { x: 2, y: 1 },
  { x: 3, y: 5 },
  { x: 6, y: 3 }
];

const StyledPoint = styled.circle`
  fill: ${(props) => props.color};
`;

const colors = ["#A8E6CE", "#DCEDC2", "#FFD3B5", "#FFAAA6", "#FF8C94"];

const ScatterPoint = ({ x, y, datum, min, max }) => {
  const i = React.useMemo(() => {
    return Math.floor(((datum.y - min) / (max - min)) * (colors.length - 1));
  }, [datum, min, max]);

return <StyledPoint color={colors[i]} cx={x} cy={y} r={6} />;
};

export default function App() {
  const temperatures = data.map(({ y }) => y);
  const min = Math.min(...temperatures);
  const max = Math.max(...temperatures);

  return (
    <VictoryChart>
      <VictoryLine data={data} />
      <VictoryScatter
        data={data}
        dataComponent={<ScatterPoint min={min} max={max} />}
      />
    </VictoryChart>
  );
}

The StyledPoint component has the points.

And ScatterPoint renders the StyledPoint with the fill color for the point.

Then we pass the ScatterPoint as the value of dataComponent to render the points.

Conclusion

We can render polygon charts and create custom components for charts with React Victory.

Categories
React

Add Charts into Our React App with Victory — Custom Labels and Points

The Victory lets us add charts and data visualization into our React app.

In this article, we’ll look at how to add charts into our React app with Victory.

Altering Default Label Components

We can override the appearance of the components that comes with Victory.

For instance, we can change the labels that are displayed with bar charts by writing:

import React from "react";
import { VictoryBar, VictoryLabel } from "victory";

export default function App() {
  return (
    <VictoryBar
      data={[
        { x: 1, y: 3, label: "Alpha" },
        { x: 2, y: 4, label: "Bravo" },
        { x: 3, y: 6, label: "Charlie" },
        { x: 4, y: 3, label: "Delta" },
        { x: 5, y: 7, label: "Echo" }
      ]}
      labelComponent={
        <VictoryLabel angle={90} verticalAnchor="middle" textAnchor="end" />
      }
    />
  );
}

We set the angle of the VictoryLabel to display the labels vertically.

textAnchor is set to end to move them to the right.

Wrapping Components

We can wrap components to display what we want.

For instance, we can create our own wrapper component to add labels to our chart:

import React from "react";
import { VictoryChart, VictoryLabel, VictoryScatter } from "victory";

const WrapperComponent = (props) => {
  const renderChildren = () => {
    const children = React.Children.toArray(props.children);
    return children.map((child) => {
      const style = { ...child.props.style, ...props.style };
      return React.cloneElement(
        child,
        Object.assign({}, child.props, props, { style })
      );
    });
  };

  return (
    <g transform="translate(20, 40)">
      <VictoryLabel text={"add labels"} x={110} y={30} />
      <VictoryLabel text={"offset data from axes"} x={70} y={150} />
      <VictoryLabel text={"alter props"} x={280} y={150} />
      {renderChildren()}
    </g>
  );
};

export default function App() {
  return (
    <VictoryChart>
      <WrapperComponent>
        <VictoryScatter
          y={(d) => Math.sin(2 * Math.PI * d.x)}
          samples={15}
          symbol="square"
          size={6}
          style={{ data: { stroke: "tomato", strokeWidth: 3 } }}
        />
      </WrapperComponent>
    </VictoryChart>
  );
}

WrapperComponent takes the child components from the children prop and render them with React.cloneElement .

We also combine the styles by merging the styles from the child and the props.

In the return statement, we have the VictoryLabel s and call the renderChildren function to render the child items.

Then in App , we render VictoryScatter in the WrapperComponent to render the sine curve with the VictoryLabel s.

Customize Points

For instance, we can create the CatPoint component and pass that as the value of dataComponent in the VictoryScatter component:

import React from "react";
import { VictoryChart, VictoryScatter } from "victory";

const CatPoint = (props) => {
  const { x, y, datum } = props;
  const cat = datum._y >= 0 ? "?" : "?";
  return (
    <text x={x} y={y} fontSize={30}>
      {cat}
    </text>
  );
};

export default function App() {
  return (
    <VictoryChart>
      <VictoryScatter
        y={(d) => Math.sin(2 * Math.PI * d.x)}
        samples={25}
        dataComponent={<CatPoint />}
      />
    </VictoryChart>
  );
}

We get the data entry from the datum prop, and the _y property has the y value.

x and y has the position.

Conclusion

We can customize labels and points with charts created with Victory in our React app.