Categories
React

Add Charts into Our React App with Victory — Custom Chart

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.

Custom Chart

We can create a custom chart by adding multiple components together.

For instance, we can write:

import React from "react";
import { VictoryAxis, VictoryLabel, VictoryLine } from "victory";

const getDataSetOne = () => {
  return [
    { x: new Date(2000, 1, 1), y: 12 },
    { x: new Date(2001, 6, 1), y: 10 },
    { x: new Date(2002, 12, 1), y: 11 },
    { x: new Date(2003, 1, 1), y: 5 },
    { x: new Date(2004, 1, 1), y: 4 }
  ];
};

const getDataSetTwo = () => {
  return [
    { x: new Date(2000, 1, 1), y: 5 },
    { x: new Date(2001, 1, 1), y: 6 },
    { x: new Date(2002, 1, 1), y: 4 },
    { x: new Date(2003, 1, 1), y: 10 },
    { x: new Date(2004, 1, 1), y: 12 }
  ];
};

const getTickValues = () => {
  return [
    new Date(2000, 1, 1),
    new Date(2001, 1, 1),
    new Date(2002, 1, 1),
    new Date(2003, 1, 1),
    new Date(2004, 1, 1)
  ];
};

const dataSetOne = getDataSetOne();
const dataSetTwo = getDataSetTwo();
const tickValues = getTickValues();

export default function App() {
  return (
    <svg viewBox="0 0 450 350">
      <VictoryLabel
        x={25}
        y={55}
        text={"Economy n % change on a year earlier"}
      />
      <VictoryLabel x={200} y={55} text={"Dinosaur exportsn $bn"} />

<g transform={"translate(0, 40)"}>
        <VictoryAxis
          scale="time"
          standalone={false}
          tickValues={tickValues}
          tickFormat={(x) => x.getFullYear()}
        />
        <VictoryAxis
          dependentAxis
          domain={[-10, 15]}
          offsetX={50}
          orientation="left"
          standalone={false}
        />
        <VictoryLine
          data={[
            { x: new Date(2000, 1, 1), y: 0 },
            { x: new Date(2004, 6, 1), y: 0 }
          ]}
          domain={{
            x: [new Date(2000, 1, 1), new Date(2004, 1, 1)],
            y: [-10, 15]
          }}
          scale={{ x: "time", y: "linear" }}
          standalone={false}
        />
        <VictoryLine
          data={dataSetOne}
          domain={{
            x: [new Date(2000, 1, 1), new Date(2004, 1, 1)],
            y: [-10, 15]
          }}
          interpolation="monotoneX"
          scale={{ x: "time", y: "linear" }}
          standalone={false}
        />
        <VictoryAxis
          dependentAxis
          domain={[0, 50]}
          orientation="right"
          standalone={false}
        />
        <VictoryLine
          data={dataSetTwo}
          domain={{
            x: [new Date(2000, 1, 1), new Date(2004, 1, 1)],
            y: [0, 50]
          }}
          interpolation="monotoneX"
          scale={{ x: "time", y: "linear" }}
          standalone={false}
        />
      </g>
    </svg>
  );
}

We get our data for the lines with the functions.

Then we add the labels with the VictoryLabel components.

We set the x and y props to set their positions.

Then we put our chart components in the g element.

VictoryAxis renders the x-axis.

We set the scale to 'time' to render the dates on the axis.

Also, we set the offsetX to shift the axis.

The first VictoryLine has renders a horizontal line.

We set the data prop to set the data.

Also, we set the x and y domain to set the range of values to show on the axis.

scale is set individually for x and y since they have different scales.

And we set standalone to false so we can add more to the chart.

Then add another VictoryLine to render the data from dataSetOne .

We add another VictoryAxis to put a y-axis on the right side. And we set their own domain.

Then add another VictoryLine to render the data from dataSetTwo.

Now we get a line chart with 3 lines, one y-axis each on each side, an x-axis at the bottom, and labels at the top of the chart.

Conclusion

We can add custom charts with the components that comes with Victory in our React app.

Categories
TypeScript

Introduction to TypeScript Interfaces

The big advantage of TypeScript over plain JavaScript is that it extends the features of JavaScript by adding type safety to our program’s objects. It does this by checking the shape of the values that objects can take on. Checking the shape is called duck typing or structural typing. It’s very useful for defining contracts within our code in TypeScript programs. In this article, we’ll look at how to define a TypeScript interface and add required or optional properties to it.

Defining Interfaces

To define a basic interface, we use the interface keyword in TypeScript. This keyword is exclusive to TypeScript and it’s not available in JavaScript. We can define a TypeScript interface like in the code below:

interface Person {
  name: string
}

In the code above, if a variable or parameter has been designated with this interface, then all objects with the type using it will have the name property. Object literals assigned to a variable with the type cannot have any other property. Arguments that are passed in as parameters that are of this type also can only have this property. For example, the following code will be compiled successfully and run with the TypeScript compiler:

interface Person{
  name: string
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}`);
}
greet({ name: 'Joe' });

The code above will compile and run since it only has the name property and the value of it is a string exactly like it’s specified in the Person interface. However, the following code wouldn’t be compiled by the TypeScript compiler and throw an error:

interface Person {
  name: string
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}`);
}

greet({ name: 'Joe', foo: 'abc' });

This is because we specified that the type of the person parameter in the greet function has the type Person. The object passed in as the argument can only have the name property and that the value of it can only be a string. We can’t assign an object literal with different properties than what’s listed in the interface if a variable has been designated by the type of the interface:

const p: Person = { name: 'Joe', foo: 'abc' };

The code above also wouldn’t compile since the object literal has an extra property in it that is not listed in the Person interface. In editors that support TypeScript like Visual Studio Code, we would get the error message “Type ‘{ name: string; foo: string; }’ is not assignable to type ‘Person’. Object literal may only specify known properties, and ‘foo’ does not exist in type ‘Person’.”

With interfaces, when designating the types of variables, we get auto-completion of our code when we write object literals.

Optional Properties

TypeScript interfaces can have optional properties. This makes interfaces much more flexible than just adding required properties to them. We can designate a property as optional with the question mark ? after the property name. For example, we can write the following code to define an interface with an optional property:

interface Person {
  name: string,
  age?: number
}

In the code above, age is an optional property since we put a question mark after the property name. We can use it as the following code:

interface Person {
  name: string,
  age?: number
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}. ${person.age ? `You're ${person.age} years old.` : ''}`);
}

greet({ name: 'Joe', age: 10 });

In the object we passed into the greet function into the code above, we passed in the age property and a number for its value. Then we used it inside our code by checking if it’s defined first and then we add additional text to the main string if it is. We can also omit optional properties like in the following code:

interface Person {
  name: string,
  age?: number
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}. ${person.age ? `You're ${person.age} years old.` : ''}`);
}

greet({ name: 'Joe' });

The code above would still run since the age property has been designated as being optional. The rules outlined by the interface still apply if we assign an object literal to it. For example, if we write:

interface Person {
  name: string,
  age?: number
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}. ${person.age ? `You're ${person.age} years old.` : ''}`);
}

const person: Person = { name: 'Joe' };
greet(person);

This would still work since we stick to the property descriptions defined in the Person interface. If we add the age property with a number property it would still compile and run:

interface Person {
  name: string,
  age?: number
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}. ${person.age ? `You're ${person.age} years old.` : ''}`);
}

const person: Person = { name: 'Joe', age: 20 }
greet(person);

Optional properties are useful in that we can define properties that can possibly be used while preventing the use of properties that aren’t part of the interface. This prevents bugs that arise because of typos in our code.

Readonly Properties

To make properties that are non-modifiable after the object is first created, we can use the readonly keyword in front of a property to designate that the property can only be written once when the object is being created and not any time after. For example, we can write the following code:

interface Person {
  readonly name: string,
  age?: number
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}. ${person.age ? `You're ${person.age} years old.` : ''}`);
}

const person: Person = { name: 'Joe', age: 20 };
greet(person);

We added the readonly keyword in front of the name property so that it can only be changed once and only once. So if we try to assign something new to the name property, the TypeScript compiler wouldn’t compile and code and the code wouldn’t run:

interface Person{
  readonly name: string,
  age?: number
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}. ${person.age ? `You're ${person.age} years old.` : ''}`);
}

let person: Person = { name: 'Joe', age: 20 };
person.name = 'Jane';
greet(person);

The code above will get us the error message “ Cannot assign to ‘name’ because it is a read-only property” when we try to compile it or in text editors that support TypeScript. However, we can reassign the whole object to a different value like in the following code:

interface Person {
  readonly name: string,
  age?: number
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}. ${person.age ? `You're ${person.age} years old.` : ''}`);
}

let person: Person = { name: 'Joe', age: 20 };
person = { name: 'Jane', age: 20 };
greet(person);

Then instead of logging ‘Hello, Joe. You’re 20 years old.’, we get ‘Hello, Jane. You’re 20 years old.’

If we want to designate an array as read-only, that is, only be changed when it’s created, we can use the ReadonlyArray type that’s the same as Array, but all the mutating methods are removed from it so that we can’t accidentally change anything in the array. For example, we can use it as in the following code:

let readOnlyArray: ReadonlyArray<number> = [1,2,3];

Then if we try to write the following code, the TypeScript compiler would give errors:

readOnlyArray[0] = 12;
readOnlyArray.push(5);
readOnlyArray.length = 100;

Then we get the following errors:

Index signature in type 'readonly number[]' only permits reading.(2542)

Property 'push' does not exist on type 'readonly number[]'.(2339)

Cannot assign to 'length' because it is a read-only property.(2540)

If we want to convert a ReadonlyArray back to a writable array, we can use the type assertion as operator to convert it back to a regular array:

let arr = readOnlyArray as number[];

Conclusion

TypeScript interfaces are very handy for defining contracts within our code. We can use it to designate types of variables and function parameters. They let us know what properties a variable can take on, and whether they’re required, optional, or read-only. We can define interfaces with the interfaces keyword, optional properties with the question mark after a variable name, and the readonly keyword for read-only properties.

Categories
React

Add Charts into Our React App with Victory — Transition, Brush and Zoom

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.

Transitions

We can set the transition effects for animations with the VictoryBar component.

For instance, we can write:

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

const random = (min, max) => Math.floor(min + Math.random() * max);

const getData = () => {
  const bars = random(6, 10);
  return Array(bars)
    .fill()
    .map((_, index) => {
      return {
        x: index + 1,
        y: random(2, 100)
      };
    });
};

export default function App() {
  const [data, setData] = useState([]);
  useEffect(() => {
    const timer = setInterval(() => {
      setData(getData());
    }, 3000);
    return () => clearInterval(timer);
  }, []);

  return (
    <VictoryChart domainPadding={{ x: 20 }} animate={{ duration: 500 }}>
      <VictoryBar
        data={data}
        style={{
          data: { fill: "tomato", width: 12 }
        }}
        animate={{
          onExit: {
            duration: 500,
            before: () => ({
              _y: 0,
              fill: "orange",
              label: "BYE"
            })
          }
        }}
      />
    </VictoryChart>
  );
}

We generate random data for our graph and update it with the new data every 3 seconds.

We set the animate prop on the VictoryBar component to add transition effects for the bars.

onExit.duration has the duration of the leave animation.

We change the color of the bars with fill property.

And the label is displayed when the bars leave the screen.

Brush and Zoom

We can add brush and zoom into our charts with the VictoryChart‘s containerComponent prop.

For instance, we can write:

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

const random = (min, max) => Math.floor(min + Math.random() * max);

const getData = () => {
  return Array(50)
    .fill()
    .map((index) => {
      return {
        x: random(1, 50),
        y: random(10, 90),
        size: random(8) + 3
      };
    });
};

export default function App() {
  return (
    <VictoryChart
      domain={{ y: [0, 100] }}
      containerComponent={
        <VictoryZoomContainer zoomDomain={{ x: [5, 35], y: [0, 100] }} />
      }
    >
      <VictoryScatter
        data={getData()}
        style={{
          data: {
            opacity: ({ datum }) => (datum.y % 5 === 0 ? 1 : 0.7),
            fill: ({ datum }) => (datum.y % 5 === 0 ? "tomato" : "black")
          }
        }}
      />
    </VictoryChart>
  );
}

We set containerComponent to the VictoryZoomContainer component.

The zoomDomain prop lets us set the zoom level range for the x and y axes with the x and y properties.

We can also create a separate chart for the brush:

import React, { useState } from "react";
import {
  VictoryAxis,
  VictoryBrushContainer,
  VictoryChart,
  VictoryLine,
  VictoryZoomContainer
} from "victory";

export default function App() {
  const [selectedDomain, setSelectedDomain] = useState();
  const [zoomDomain, setZoomDomain] = useState();

const handleZoom = (domain) => {
    setSelectedDomain(domain);
  };

const handleBrush = (domain) => {
    setZoomDomain(domain);
  };

return (
    <>
      <VictoryChart
        width={550}
        height={300}
        scale={{ x: "time" }}
        containerComponent={
          <VictoryZoomContainer
            responsive={false}
            zoomDimension="x"
            zoomDomain={zoomDomain}
            onZoomDomainChange={handleZoom}
          />
        }
      >
        <VictoryLine
          style={{
            data: { stroke: "tomato" }
          }}
          data={[
            { x: new Date(1982, 1, 1), y: 125 },
            { x: new Date(1987, 1, 1), y: 257 },
            { x: new Date(1993, 1, 1), y: 345 },
            { x: new Date(1997, 1, 1), y: 515 },
            { x: new Date(2001, 1, 1), y: 132 },
            { x: new Date(2005, 1, 1), y: 305 },
            { x: new Date(2011, 1, 1), y: 270 },
            { x: new Date(2015, 1, 1), y: 470 }
          ]}
        />
      </VictoryChart>

<VictoryChart
        width={550}
        height={90}
        scale={{ x: "time" }}
        padding={{ top: 0, left: 50, right: 50, bottom: 30 }}
        containerComponent={
          <VictoryBrushContainer
            responsive={false}
            brushDimension="x"
            brushDomain={selectedDomain}
            onBrushDomainChange={handleBrush}
          />
        }
      >
        <VictoryAxis
          tickValues={[
            new Date(1985, 1, 1),
            new Date(1990, 1, 1),
            new Date(1995, 1, 1),
            new Date(2000, 1, 1),
            new Date(2005, 1, 1),
            new Date(2010, 1, 1),
            new Date(2015, 1, 1)
          ]}
          tickFormat={(x) => new Date(x).getFullYear()}
        />
        <VictoryLine
          style={{
            data: { stroke: "tomato" }
          }}
          data={[
            { x: new Date(1982, 1, 1), y: 125 },
            { x: new Date(1987, 1, 1), y: 257 },
            { x: new Date(1993, 1, 1), y: 345 },
            { x: new Date(1997, 1, 1), y: 515 },
            { x: new Date(2001, 1, 1), y: 132 },
            { x: new Date(2005, 1, 1), y: 305 },
            { x: new Date(2011, 1, 1), y: 270 },
            { x: new Date(2015, 1, 1), y: 470 }
          ]}
        />
      </VictoryChart>
    </>
  );
}

We do this by setting the zoomDomain and the selectedDomain by dragging on the bottom graph.

In the bottom VictoryChart , we have the containerComponent set to the VictoryBrushContainer .

brushDomain is set to the selectedDomain ,

And the onBrushDomainChange is set to the handleBrush function to change selectedDomain .

The location will be reflected in the top chart.

We have similar code to set the zoomDomain in the top chart.

The zoom is reflected in the bottom chart.

Conclusion

We can add transitions, brush, and zoom into charts in our React app with Victory.

Categories
React

Add Charts into Our React App with Victory — Gradient and Image Background, and Animations

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.

Gradient Background for Polar Charts

We can add gradients for polar charts with the radialGradient component/

For instance, we can write:

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

export default function App() {
  return (
    <div>
      <svg>
        <defs>
          <radialGradient id="radial_gradient">
            <stop offset="10%" stopColor="red" />
            <stop offset="95%" stopColor="gold" />
          </radialGradient>
        </defs>
      </svg>
      ​
      <VictoryChart
        polar
        style={{
          background: { fill: "url(#radial_gradient)" }
        }}
      >
        <VictoryScatter />
        <VictoryPolarAxis
          style={{
            tickLabels: { angle: 0 }
          }}
          tickValues={[0, 90, 180, 270]}
        />
      </VictoryChart>
    </div>
  );
}

We add the stop component with the offset prop to set the amount of the color to see in the gradient.

The VictoryPolarAxis component lets us add the axis for the chart.

The VictoryChart component has the polar prop to add a polar chart/

Charts with Images

We can add charts with background images by setting the backgroundComponent prop of VictoryChart .

For instance, we can write:

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

const CustomBackground = (props) => {
  return <image href={"https://picsum.photos/id/906/525/300.jpg"} {...props} />;
};

const Matterhorn = (props) => {
  return (
    <VictoryChart
      domain={{ y: [2000, 5000] }}
      style={{ background: { opacity: 0.8 } }}
      backgroundComponent={<CustomBackground />}
    >
      <VictoryLine
        data={[
          { x: 0, y: 2500 },
          { x: 1.25, y: 2600 },
          { x: 1.8, y: 3000 },
          { x: 2.7, y: 3300 },
          { x: 3.1, y: 3800 },
          { x: 3.25, y: 4000 },
          { x: 3.5, y: 4000 },
          { x: 4, y: 4478, label: "4,478m" },
          { x: 4.5, y: 4300 },
          { x: 5.1, y: 4200 },
          { x: 6.3, y: 3500 },
          { x: 6.75, y: 3400 },
          { x: 7, y: 3300 },
          { x: 7.25, y: 3200 },
          { x: 9, y: 2900 },
          { x: 12, y: 2000 }
        ]}
        style={{
          data: { strokeWidth: 4 }
        }}
      />
      <VictoryAxis dependentAxis />
    </VictoryChart>
  );
};

export default function App() {
  return <Matterhorn />;
}

We add the CustomBackground component and set that as the value of the backgroundComponent to display the image as the background image.

Animations

We can add animations to our Victory charts with the animate prop.

For instance, we can write:

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

const random = (min, max) => Math.floor(min + Math.random() * max);

const getScatterData = () => {
  const colors = [
    "violet",
    "cornflowerblue",
    "gold",
    "orange",
    "turquoise",
    "tomato",
    "greenyellow"
  ];
  const symbols = [
    "circle",
    "star",
    "square",
    "triangleUp",
    "triangleDown",
    "diamond",
    "plus"
  ];
  return Array(25)
    .fill()
    .map((index) => {
      const scaledIndex = Math.floor(index % 7);
      return {
        x: random(10, 50),
        y: random(2, 100),
        size: random(8) + 3,
        symbol: symbols[scaledIndex],
        fill: colors[random(0, 6)],
        opacity: 0.6
      };
    });
};

export default function App() {
  const [data, setData] = useState([]);
  useEffect(() => {
    const timer = setInterval(() => {
      setData(getScatterData());
    }, 3000);
    return () => clearInterval(timer);
  }, []);

  return (
    <VictoryChart animate={{ duration: 2000, easing: "bounce" }}>
      <VictoryScatter
        data={data}
        style={{
          data: {
            fill: ({ datum }) => datum.fill,
            opacity: ({ datum }) => datum.opacity
          }
        }}
      />
    </VictoryChart>
  );
}

to set the animate prop to set the duration and easing of the animation.

Whenever we change the data state, the chart will re-render with the new data values.

getScatterData just returns an array of values for the scatterplot.

Conclusion

We can add gradients for the polar chart and animations to charts in our React app with Victory.

Categories
React

Add Charts into Our React App with Victory — Histograms and Scatterplots

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.

Histogram

We can add a histogram into our React app with Victory’s VictoryHistogram component.

For instance, we can write:

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

const data = [
  { x: 0 },
  { x: 1 },
  { x: 1 },
  { x: 1 },
  { x: 1 },
  { x: 2 },
  { x: 2 },
  { x: 3 },
  { x: 4 },
  { x: 7 },
  { x: 7 },
  { x: 10 }
];

export default function App() {
  return (
    <VictoryChart>
      <VictoryHistogram
        style={{ data: { fill: "#F1737F" } }}
        cornerRadius={3}
        data={data}
      />
    </VictoryChart>
  );
}

to add the VictoryHistogram component to add the histogram.

The cornerRadius lets us set the corner radius.

And we set the bar’s fill color with the data.fill property.

We can change the bins with the bins prop:

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

const data = [
  { x: 0 },
  { x: 1 },
  { x: 1 },
  { x: 1 },
  { x: 1 },
  { x: 2 },
  { x: 2 },
  { x: 3 },
  { x: 4 },
  { x: 7 },
  { x: 7 },
  { x: 10 }
];

export default function App() {
  return (
    <VictoryChart>
      <VictoryHistogram
        style={{ data: { fill: "#F1737F" } }}
        cornerRadius={3}
        bins={[0, 2, 8, 15]}
        data={data}
      />
    </VictoryChart>
  );
}

And we can stack histograms with the VictoryStack component:

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

const data1 = [
  { x: 0 },
  { x: 1 },
  { x: 1 },
  { x: 1 },
  { x: 1 },
  { x: 2 },
  { x: 2 },
  { x: 3 },
  { x: 4 },
  { x: 7 },
  { x: 7 },
  { x: 10 }
];

const data2 = [
  { x: 0 },
  { x: 1 },
  { x: 1 },
  { x: 1 },
  { x: 2 },
  { x: 2 },
  { x: 3 },
  { x: 4 },
  { x: 5 },
  { x: 7 },
  { x: 8 }
];

export default function App() {
  return (
    <VictoryChart>
      <VictoryStack colorScale="qualitative">
        <VictoryHistogram data={data1} />
        <VictoryHistogram cornerRadius={3} data={data2} />
      </VictoryStack>
    </VictoryChart>
  );
}

We just put the VictoryHistogram s in the VictoryStack component.

Scatterplots

We can create scatterplots with the VictoryScatter component.

For instance, we can write:

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

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

export default function App() {
  return (
    <VictoryChart
      style={{
        background: { fill: "lavender" }
      }}
    >
      <VictoryScatter data={data} />
    </VictoryChart>
  );
}

We can set the background of the scatterplot with the background.fill property.

And we add the scatterplot with the VictoryScatter property.

Conclusion

We can add a histogram and scatterplot into our React app with Victory.