Skip to main content
Version: 1.x.x

Storing Custom Data in Records

quip.apps.Record objects are the basic units of data storage in the Quip API.

Adding new Properties

Generally, the process for storing data is as follows:

  1. Create a new record or identify a record that you would like to add data to. All apps are initialized with a RootRecord, which is an appropriate place to store simple data. The process for storing data on all Record subclasses is identical.

  2. Add a property for the data you'd like to store. The type for this property should be as specific as possible. See the Record docs for a list of availble data types. In this example, we'll store a number that will act as a counter:

static getProperties() {
return {
counter: "number"
};
}

if you'd like to make sure the property always has a value (to avoid null checking), you can define a default property as well:

static getDefaultProperties() {
return {
counter: 0
}
}
  1. To read the value at runtime, use myRecord.get("counter"). When using typescript, it's recommended that you cast the return type, as get just returns any. For most properties, we recommend that you write a getter rather than access the property directly:
getCounter = () => this.get("counter") as number
  1. When you'd like to write the value, use myRecord.set("counter", someValue). When using typescript, we also recommend that you write setters for values that are mutable:
setCounter = (value: number) => this.set("counter", value)

That's all there is to storing data - however, remember that in Quip, data can be updated by anyone, and your application should respond to these changes by listening to data. A common pattern in Quip is to use listeners and state to track your data model in real time whenever it changes. In fact, the boilerplate does this for you automatically.

Syncing your state with your RootRecord

Here's an example of a simple application that syncs its state with RootRecord:

import React from "react"
import quip from "quip-apps-api"

interface AppData {
counter: number;
}

class Root extends quip.apps.RootRecord {
static getProperties() {
return {
counter: "number"
};
}
static getDefaultProperties() {
return {
counter: 0
}
}

getCounter = () => this.get("counter") as number
setCounter = (value: number) => this.set("counter", value)

getData(): AppData {
return {
counter: this.getCounter()
}
}
}

interface CounterAppProps {
root: Root;
}

interface CounterAppState {
data: AppData;
}

class CounterApp extends React.Component<CounterAppProps, CounterAppState> {
constructor(props: CounterAppProps) {
super(props);
// initialize our state to whatever the state is when we instantiate
// this component.
const {root} = this.props;
const {data} = root.getData();
this.state = {data};
}
componentDidMount() {
// when our component mounts, begin listening to changes on the root
// record. This will make sure that when the root record changes, our
// state will stay up to date (even if it is changed remotely by
// another user!).
const {root} = this.props;
root.listen(this.updateData);
// When adding a listener, make sure that the initial state we show is
// accurate.
this.updateData();
}
componentWillUnmount() {
// when our component unmounts, remove our listener so we don't leak.
const {root} = this.props;
root.unlisten(this.updateData);
}

private updateData = () => {
const {root} = this.props;
this.setState({data: root.getData()});
}

render() {
const {root} = this.props;
// Since our data is in our state now, this component will
// automatically re-render whenever our data is changed - either by
// us, or by someone else.
const {counter} = this.state.data;
return <div onClick={() => root.setCounter(counter + 1)}>
<h1>Clicked {counter} times!</h1>
</div>
}

}

quip.apps.initialize({
initializationCallback: async (dom, params) => {
const root = quip.apps.getRootRecord() as Root;
ReactDOM.render(<CounterApp root={root}/>, dom);
}
});

More Documentation

The Record Reference Documentation explains in-depth exactly how to use Records, including RecordList, ImageRecord, and RichTextRecord.