Jun 22, 2020 10:27 AM
The Custom Blocks contest deadline is approaching! Do you have questions about releasing or debugging your block? We’re hosting our third live Ask Me Anything (AMA) session in this thread on Wednesday (6/24) at 11am PT.
Billy Littlefield and Tim Deng from our platform engineering team will be ready to answer any development questions you have.
We’ll be picking 3 questions at random to win a $40 Amazon gift card. Any solutions offered by the community will be included in the draw as well. You can even start posting your questions now, and we’ll be answering questions live from 11am - 11:30am PT on Wednesday. See you there!
Jun 24, 2020 11:14 AM
When invoking a custom block from a button, is there a way to tell when a button has been pressed again? I can tell if a button for a different record has been pressed because I can compare the current record with the previous record, but I would also like to be able to tell if the button for the same record has been pressed again.
Use case: the user presses the button to start a wizard, which progresses through some screens. If the user presses the button again, I would like to start the wizard over again from the beginning.
Jun 24, 2020 11:16 AM
I think what Kasra was getting at is closer to your latter point – writing unit tests against stateless logic which doesn’t interact directly with the base (e.g. testing data manipulation logic that happens before sending to the base or after fetching from the base). You could use mock data and/or stubs to help seed the functions you’re testing.
Improving the testability of blocks & coming up with a more concrete guide of best practices is something we’re thinking about for the future!
Jun 24, 2020 11:20 AM
Is there a way for a block to put up an alert outside the context of the block itself?
For example, if a block is monitoring a table and notices an error condition, is there some way for the block to notify the user, even if the user does not look at the block? Modal dialog boxes seem to work only in the context of the block, and
alert
is very ugly and probably best for debugging purposes.
Unfortunately that isn’t possible; blocks operate in an iframe with a separate context from the rest of the page. We’re thinking about other ways to provide notifications (e.g. toasts), but that isn’t something that currently exists.
Jun 24, 2020 11:23 AM
I have a button that creates a new record using
createRecordAsync()
when clicked. I would like to open the newly created record once this is done usingexpandRecord()
, butcreateRecordAsync()
only provides the record ID and not the record object. Loading the new record usinguseRecordById()
seems to break hook rules; this is all in an async function. Any advice?
Jeremy’s answer should solve this – if you store the newly created record ID in some local state, then on a future re-render the records returned from useRecords
should contain the new record, and you can call expandRecord
.
If you did want to fetch the new record in the same block of code, I think you could do so doing something like this:
const newRecordId = await table.createRecordAsync(...)
const freshQueryResult = await table.selectRecordsAsync()
expandRecord(freshQueryResult.getRecordById(newRecordId))
But depending on how often this gets called, it may be better to just wait for the next render cycle.
Jun 24, 2020 11:25 AM
it may be better to just wait for the next render cycle.
What determines how soon a new render occurs?
Jun 24, 2020 11:28 AM
How do you recommend showing a <Loader scale={0.3} />
while doing a fetch
?
Do I use state to keep track of when the fetch has been sent, and then change state when the response is received? Is there an easier way?
Jun 24, 2020 11:29 AM
I noticed that when I click the “Custom” button next to the name of a custom block, it shows
- the name of the block developer (based on the Airtable account)
- the email address of the block developer (based on the Airtable account)
- the date of the last update
Is all of this information set automatically? Can we change it to display a different name or email address?
That information is set automatically, by the person who last runs block release
. Like you noted, it’s tied to an Airtable account so you can’t manually edit it (but you can have a creator on your base release a version of the block to change it to their name and email).
Jun 24, 2020 11:37 AM
I noticed that there is a version number in
block.json
. Is this version number displayed to the block user anywhere?Are there guidelines for where to display version information to the user?
Could it be included along with the contact info for the block developer when the “Custom” button is clicked?
That version isn’t exposed anywhere, but can be used internally (e.g. to supplement your commit history). When you block release
, all installations of that block will reference the new code, so it isn’t possible for users to be on different versions of the same block currently. We’re thinking about ways to expose when a new release has been pushed up to the consumer!
Under the hood, versioning is still something that can be done - for example if you there are required schema changes you’d like to enforce upon a new release. One strategy you could take here is to store it version in GlobalConfig, then perform some logic that runs in your root component and can perform necessary operations if needed –
e.g.
if (globalConfig.get('blockVersion') < 2) {
globalConfig.setPathsAsync([
{path: 'someNewPath', value: 'default'},
{path: 'blockVersion', value: 2}
])
}
This could be wrapped into a custom useBlockVersion
hook or similar.
Jun 24, 2020 11:44 AM
What determines how soon a new render occurs?
It largely depends on how the block is architected, but here are some examples –
If you’re using a standard react useState
hook, a rerender will trigger anytime you modify the state using the returned setter (e.g. const [value, setValue] = useState(0)
whenever setValue is called).
If you’re using the useRecords
hook, a rerender will fire anytime a record is deleted / added in the base, or cell values change, or record colors change (this includes changes that propagate from the block itself). For the hooks provided by the SDK, this is determined by the watchable keys. This is a little in the weeds, but you can see which watchable keys useRecords
uses here. You can even define your own triggers by using useWatchable on an Airtable model directly
Jun 24, 2020 11:58 AM
Several times when I try to create a new block from the command line, I get
npm enoent
errors, and can’t create the new block. I can’t tell any difference between when it works and when it doesn’t. Sometimes I have to come back hours later and try again.Any insight?
Hmm, I’m not sure what might be causing this off the top of my head. This is on block init
? If this continues occurring or you’re able to reproduce more reliably, please write in to blocks@airtable.com so we can try to debug further
Jun 24, 2020 12:01 PM
It looks like the
.block
folder lists remotes, including base app ids. I’d rather not include my app ids in a public git repository.However, this folder is not included in the .gitignore file by default.
What are the implications of adding this folder to the .gitignore file?
How hard will it be for someone to re-create my custom block if I add this folder to .gitignore?
Adding your .block
folder to your git ignore will prevent it from syncing to your git remote, but it’ll still be on your local computer. If you’re the only one working on the custom block and you only use one computer, this isn’t an issue (otherwise, everyone else collaborating on your block will need the same app and base IDs). If you’re concerned about other users, ignoring your .block
folder should be fine! They can choose “Remix from GitHub” when creating a custom block, and it will automatically create a .block
folder for them.
Jun 24, 2020 12:11 PM
When invoking a custom block from a button, is there a way to tell when a button has been pressed again? I can tell if a button for a different record has been pressed because I can compare the current record with the previous record, but I would also like to be able to tell if the button for the same record has been pressed again.
Use case: the user presses the button to start a wizard, which progresses through some screens. If the user presses the button again, I would like to start the wizard over again from the beginning
Ah, interesting use case. With the useRecordActionData
hook, the data always will be the latest press, so you wouldn’t register a separate click. I think you can register a callback directly, though, using registerRecordActionDataCallback which should execute the registered callback on every push (even if its the same button).
I wonder if there’s value in providing a timestamp or ID in the action payload to distinguish between these clicks using the hook.
EDIT: Another solution here would be to use a usePrevious
hook, e.g.
function usePrevious(value) {
const ref = React.useRef();
React.useEffect(() => {
ref.current = value;
});
return ref.current;
}
This way, you can compare the value returned by useRecordActionData()
for subsequent presses. Even if the same record is clicked, the object returned by useRecordActionData
on a subsequent button press will be a new object compared to the value cached by usePrevious
.
function MyComponent() {
const buttonData = useRecordActionData();
const lastButtonData = usePrevious(buttonData);
useEffect(() => {
if (buttonData !== lastButtonData && buttonData) {
// perform logic for button press
}
}, [buttonData, lastButtonData])
// ... rest of component ...
}
Jun 24, 2020 12:16 PM
How do you recommend showing a
<Loader scale={0.3} />
while doing afetch
?
Do I use state to keep track of when the fetch has been sent, and then change state when the response is received? Is there an easier way?
I think that’s the most logical way – barebones example:
function MyComponent() {
const [isLoading, setIsLoading] = useState(false);
function fetchTheThing() {
setIsLoading(true);
fetch('example.com').then(response => {
// do stuff with response
setIsLoading(false);
})
}
return (
<Box>
{isLoading ? (
<Loader />
) : (
<RestOfApp />
)}
</Box>
)
}
Jun 24, 2020 12:27 PM
It appears that in order to have a remote release of a block, the block author needs to be a collaborator with creator permissions in the target base. Can you confirm this?
Is there a way to install a custom block without the developer being a creator?
Does the block remain after the developer/creator is no longer a collaborator?
Correct, currently only creators can create, release, and install blocks in a given base. It’s not currently possible to install a custom block without creator permissions. If the developer/creator is removed from the base, the block will remain. Releasing, publishing, and sharing blocks is something we’re thinking about for the future in order to reduce friction for both the developer and consumers.
Jun 24, 2020 12:42 PM
And that’s a wrap! If you have any further questions, you can always find us in the custom blocks beta forum. The winners for this week are @Yuriy_Klyuch, @kuovonne, and @Kirill_Madorin!
With the contest deadline fast approaching (July 6th), we’ll be hosting our last AMA in the custom blocks contest series next week with a twist! Instead of AMA, we’ve got a fun “AYA” format planned. The post will be up EoD Friday and will be open until 6pm PT on Thursday, July 1. We’ll be giving out Amazon gift cards to three of the most insightful posts. Stay tuned!
Jun 24, 2020 12:42 PM
Hi
I’ve just finished a very first version of Image Annotation block. Here is a demo: https://www.youtube.com/watch?v=1O6hSDny2TA . Could you give me some feedback on what to improve before releasing it?
And some more quesitons:
- Is it possible to specify not only fields, but also field values in RecordQueryResultOpts? For example, to get only records which have a specific value in a specific field?
- How can I create a table with a field that will be linked to another table? What options should I use when calling 'base.unstable_createTableAsync`
- Is it possible to import a css file in the component? Can I extend the build/dev configs somehow?
- Do you provide some tools to log errors in Custom Blocks or it will be better to use something like Sentry for now?
Exciting to see your block in action! I’ll have to refrain from providing feedback now, assuming this is an entrant in the current blocks contest. Happy to answer your questions, though!
RecordQueryResultOpts
, but if you create a view in the main product that performs this filtering, you can select records from just that view.