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.
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.
1# Storing an X/Y coordinate pair in a tuple
2coordinates = (-100, 260)
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:
1const coordinates = [-100, 200];
2const [x, y] = coordinates;
3
4console.log(x); // prints -100
5console.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.
1function lowAndHigh(items) {
2 items.sort((a, b) => a - b);
3 return [
4 items[0], // lowest value
5 items[items.length - 1] // highest value
6 ];
7}
8
9const [low, high] = lowAndHigh([23, 10, 49, 7]);
10console.log(low); // prints 7
11console.log(high); // prints 49
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.
1import React, { useState } from 'react';
2
3function Counter() {
4 const [count, setCount] = useState(0);
5
6 return (
7 <>
8 <p>You clicked {count} times</p>
9 <button onClick={ () => setCount(count + 1) }>
10 Click me
11 </button>
12 </>
13 );
14}
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.
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:
1<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
2<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
3
4<div id="react"></div>
5
6<script type="text/babel">
7
8const { useState } = React;
9
10function MyCustomComponent({ triggerQuery, model, modelUpdate }) {
11 const [count, setCount] = useState(0);
12
13 return (
14 <div>
15 <p>You have clicked {count} times</p>
16 <button onClick={ () => setCount(count + 1) }>
17 click me
18 </button>
19 </div>
20 );
21}
22
23// This is the entrypoint for the React component.
24const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
25const container = document.getElementById('react');
26const root = ReactDOM.createRoot(container);
27root.render(<ConnectedComponent />);
28
29</script>
30
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.
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:
1<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
2<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
3
4<div id="react"></div>
5
6<script type="text/babel">
7
8function MyCustomComponent({ triggerQuery, model, modelUpdate }) {
9 return (
10 <div>
11 <p>You have clicked {model.count} times</p>
12 <button onClick={ () => modelUpdate({ count: model.count + 1 }) }>
13 click me
14 </button>
15 </div>
16 );
17}
18
19// This is the entrypoint for the React component.
20const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent);
21const container = document.getElementById('react');
22const root = ReactDOM.createRoot(container);
23root.render(<ConnectedComponent />);
24
25</script>
26
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".
- Day 1: A parser for an abstract syntax tree
- Day 2: Two tuple hacks
- Day 3: Three equal signs
- Day 4: Four CRUD operations
- Day 5: Five Sidebar Wins
- Day 6: Six Commands in a Palette
- Day 7: Seven Big Fish Swimming
- Day 8: Eight Bits of Gaming
- Day 9: Nine Star Wars Movies
- Day 10: Ten Retool Videos
- Day 11: Eleven Best FIFA World Cup Games
- Day 12: Twelve Lines of AI Poetry
Reader