Welcome back for day two of 12 Days of Retool! Yesterday, we learned about abstract syntax trees and how to include external scripts in Retool apps. If you missed that post, or any other one during the 12 Days of Retool, a link for every post can be found at the bottom of this page.

Today, we'll show how and when to use a tuple (or at least a tuple-like array) in JavaScript for two specific kinds of data, and share one sample Retool app that includes a notable example of "tuples" in a custom component.

What is a tuple?

A tuple is an ordered list of items, like an array or linked list. In many programming languages (like Python), a tuple is a primitive data type that can be used to represent sequential data. But unlike many other list data types, tuples are immutable. Making this data immutable can be convenient for a programmer when data consistency or performance optimization are required. In Python, you would declare a tuple using parentheses and comma-separated values.

# Storing an X/Y coordinate pair in a tuple
coordinates = (-100, 260)

Tuples in JavaScript

As of December 2022, JavaScript does not have an actual tuple as a primitive data type, though there is an ongoing proposal to create both tuples and records. For use cases that could benefit from a tuple-like data structure, JavaScript developers must use an array of a documented length with data in a meaningful position semantically.

If JavaScript doesn't have tuples, when is it beneficial to pretend as if they do exist? This technique is handy in two situations:

  • When you'd like to pass around data that is naturally related using a single object reference
  • When position has actual meaning for the data being handled

In the Python example above, we created a tuple representing an X/Y coordinate pair. Having the X and Y values follow one another in a sequence is a natural way to represent this data, because 2D coordinates are generally given in this format in mathematics. In JavaScript, we can conveniently assign positional data to variables using array destructuring:

const coordinates = [-100, 200];
const [x, y] = coordinates;

console.log(x); // prints -100
console.log(y); // prints 200

In addition to representing data that is naturally sequential, a tuple can also be a useful way of grouping together data that is naturally connected in a single object reference. You might have a function which takes an array of numbers, and returns both the highest and the lowest values from the list. You could return a "tuple" that allows the consumer of the function to easily access both values.

function lowAndHigh(items) {
  items.sort((a, b) => a - b);
  return [
  	items[0], // lowest value
    items[items.length - 1] // highest value
  ];
}

const [low, high] = lowAndHigh([23, 10, 49, 7]);
console.log(low); // prints 7
console.log(high); // prints 49

Tuples in React (and Retool Custom Components)

A popular usage of tuples that JavaScript developers may be familiar with can be found in React Hooks, in particular the state hook. The state hook returns a state variable and a function to set that state variable as a pair of values in an array, designed to leverage destructuring in JavaScript code.

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <>
      <p>You clicked {count} times</p>
      <button onClick={ () => setCount(count + 1) }>
        Click me
      </button>
    </>
  );
}

If you create a custom component for a Retool application, chances are good that you'll use this pattern there as well. If you're curious, you can see how a custom React component, using the useState hook, works in the context of a Retool application (live example). You can download the application JSON here, which you can then import into your own Retool instance.

Two examples of state management in a Retool custom component

The example on the left uses the useState hook, and returns the state management tuple interface that we just saw. Here's the code driving the custom component on the left:

<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="react"></div>

<script type="text/babel">

const { useState } = React;

function MyCustomComponent({ triggerQuery, model, modelUpdate }) {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>You have clicked {count} times</p>
      <button onClick={ () => setCount(count + 1) }>
        click me
      </button>
    </div>
  );
}

// This is the entrypoint for the React component.
const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
const container = document.getElementById('react');
const root = ReactDOM.createRoot(container);
root.render(<ConnectedComponent />);

</script>

For the sake of completeness, I also added a second example on the right that shows how you'd typically manage state in a Retool app with a custom component. Rather than using the state hook to manage state locally within the context of the iframe that houses a custom component, you would use the "model" state configured in the Retool application UI.

Using state managed by the wrapping Retool application

This component code doesn't contain a tuple, but instead uses the "model" object and "modelUpdate" function to manage state that is shared across the component and the parent Retool application. Here is the code driving the custom component on the right:

<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="react"></div>

<script type="text/babel">

function MyCustomComponent({ triggerQuery, model, modelUpdate }) {
  return (
    <div>
      <p>You have clicked {model.count} times</p>
      <button onClick={ () => modelUpdate({ count: model.count + 1 }) }>
        click me
      </button>
    </div>
  );
}

// This is the entrypoint for the React component.
const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
const container = document.getElementById('react');
const root = ReactDOM.createRoot(container);
root.render(<ConnectedComponent />);

</script>

Tuple to tango

Today, we learned about the tuple as a data structure, and how to approximate it in JavaScript. We also learned when a tuple might be useful, and shared a few practical examples in React (in the context of a Retool custom component).

I hope you enjoyed today's hack! Below, you'll find a list of every post from the 12 Days of Retool. Make sure to swing by tomorrow when we tackle type coercion with "Three Equal Signs".

All 12 Days of Retool posts