Blocks

React API

A block is a component that can render anything that is connectable, as well as users and groups. Blocks are usually found alongside other blocks in a grid-like layout, and further backing this, don't contain any intrinsic dimensions besides being a flex element.

Every variant of a block can be expressed as an enum like this:

enum BlockVariant {
  user,
  channel,
  group,
  text,
  image,
  link,
  embed,
  attachment,
}

An empty block can be created by rendering a Block with no props:

<Block />

An empty block is used as a loading state, as well as a data-error state.

To render a block with actual content, you pass in 2 props:

type BlockProps = {
  id: number,
  variant: BlockVariant,
}

These two props are all that's required to render every type of block in Are.na. You don't need to pass in a photo URL for image blocks or a filename for attachments. The entry-point for each block is exactly the same for every variant and has been reduced to the smallest representation possible. This keeps the actual React application tree extremely small and readable, and also has performance benefits since the id and variant props are both primitive values that keep reference equality between render cycles.

Since blocks are rendered at such a high rate, performance optimization like maintaining a small memoizable API are extremely important. Other optimizations have been considered as well, the 2nd biggest one probably being that blocks are not allowed to make network requests. The networking responsibility is delegated up to the containing grid element. This leads to the question: How does a block's actual content get injected into the component if it's not passed down by props or loaded from a network?

Querying block contents

arena-next was built in a way that a cache of graphql queries made by Apollo is stored in a user's browser, which persists between page transitions. As a user makes more and more graphql queries, Apollo builds up this cache in such a way that a second graph is created in the browser, which is shaped like a subset of the graph that exists on the server.

A block is intrinsically expected to be rendered along-side other blocks in a grid-like container. Since that grid component is in charge of rendering many blocks, it leads to the idea that it can also be in charge of making paginated network requests to the server based on which blocks are being rendered at that time.

Inside of a block, we still use the exact same GraphQL query API but specify that we can only make queries to the cache. The block's query shares the same exact data fragment as the grid's pagination query, so we can be certain that the block's requested data will be correctly loaded. Apollo ensures that query-data maintains reference equality and immutability, which means that as the graph continues the grow and be modified, a block will only re-render if its associated data changes.

To me this feels like a very graph-first way of building an application, where the design of the graph is at the forefront of the frontend code instead of being obfuscated by a middle layer of props or global state.

Two more ways to explore arena are now live, both relating to channels. A good starting point to test it out is the checking out the channel "cool channel names/channels I like" and following through to all the connections.

  1. The channel page header now has the channel owner in its taxonomy, with the ability to navigate to the owner's page (clicking only works for users now, not groups).

  2. You can now click on a channel block to be taken to its channel page.

It's hard for me to describe the underlying data-structure of arena, but something so clear is that it has an insanely wide breadth, and the ease of exploring the breadth of it is what makes it so powerful. I'm slowly starting to feel like this project is hitting this same feeling of discovery and that's super exciting & motivating for me.

Channel pages, at their most bare form, are live: arena-next.vercel.app/channel/arena-influences. Fast follow will be displaying the channel description & owner taxonomy in the header.

So much engineering work went into the blocks, particularly related to how they query the Apollo cache for data instead of having them passed down as props in React. Excited to write in depth about this later. Besides big code changes, the blocks now don't have a white background for visual clarity, and their text size is smaller.

(imo), the coolest feature of every page so far is there are literally 0 layout shifts after a page loads. Everything that is rendered serverside determines the final skeleton of the page. This means that when you click into a block & explore its subsequent pages, you'll still have your scroll position on every page in your history when you navigate backwards.

···