-
Notifications
You must be signed in to change notification settings - Fork 25
Lesson 1.8
This lesson will teach you the following:
- Airtable
- Data Fetching
With Hooks, it became possible for function components to manipulate state, and have access to a few other parts of React that only class components could use until then.
One of those parts of React is the ability to run code after a component has rendered.
A class component has access to component lifecycle methods such as ComponentDidMount, ComponentDidUpdate, ComponentWillUnmount. These methods only allow class components access to the mounting, updating, and unmounting events of a component.
For example, when the user lands on the page, the component is mounted, then as the states updates, the component is also updated, and, finally, the component is unmounted when the user leaves the page.
But what about functional components?
The useEffect hook gives you the ability to run code after a function component has rendered. By default, it runs after render, and after each update. This default behavior has particularly nasty consequences when writing a useEffect that could trigger a render.
For example, your useEffect triggers a render, the component renders, and useEffect runs again - causing an infinite loop.
Optionally, you can provide an array of dependencies (both variables and functions) for useEffect to watch for changes, and only run your code when any of them change.
useEffect(() => {
// Mounting
return () => {
// Cleanup function
}
}, [//Updating])
The first part is the mounting part. That's when the component is initially rendered when the user lands on the page. The return function is the cleanup function, or when the user leaves the page and the component will unmount.
The array is the last part, and it is where you put the states that will update throughout the component's lifecycle.
The useEffect hook is useful when you want to update the UI when state changes. You can also define a state on first load (i.e. componentDidMount), and also clean the state when the component is unmounting (componentWillUnmount).
import React, { useState } from "react"
const Button = () => {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>You clicked {count} times</button>;
}
export default Button
It's very common, when building an application, to fetch data from an API.
When a user lands on our page, we want to call the API. In other words, we want to call the API during the mounting part of the component's lifecycle.
Note: We will use the use-case of connecting and writing to the Airtable API in our examples:
We connect to the API with the proper key and authorization via the Fetch API. Responding to server error(s) are handled by the try/catch but if there is a 4xx client error, checking the** response.ok **property tells us if the response was successful or not.
After fetching the response object, we first need to convert the data stream to JSON with response.json(). We must retain the id field returned by the Airtable API so it can be used for CRUD operations such as writing, updating and deleting.
The data stored in a table is returned in a fields object with each property a field in the table you created earlier. In this example, there is just one property of name stored in fields.
After retrieving the data it can them be used in local state of whichever component needs to store it to be re-rendered to the UI.
const loadTodos = async() => {
try {
const response = await
fetch(`https://api.airtable.com/v0/{API key}`, {
headers: {
'Authorization': `Bearer ${API_KEY}`
}
});
if (!response.ok) {
const message = `Error: ${response.status}`;
throw new Error(message);
}
const todosFromAPI = await response.json();
const todos = todosFromAPI.records.map((todo) => {
const newTodo = {
id: todo.id,
name: todo.fields.name
}
return newTodo
});
setTodos(todos);
} catch (error) {
console.log(error.message)
}
With creating using a POST method, the Airtable API requires new record(s) to be wrapped in an object within a fields property. The data sent to the Airtable API will be sent in the body of the response.
const postTodo = async (todo) => {
try {
const airtableData = {
fields: {
name: todo
}
}
const response = await
fetch(`https://api.airtable.com/v0/{API key}/todos`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify(airtableData)
});
if (!response.ok) {
const message = `Error has ocurred:
${response.status}`;
throw new Error(message);
}
const dataResponse = await response.json();
return dataResponse;
} catch (error) {
console.log(error.message);
return null;
}
}
- Sign up (or login) for an Airtable account: https://airtable.com/signup
- Create a new base
- Choose a Title, Icon, and Color for your base
- Rename "Table 1" to "Default"
- Within the table, rename the first column to "Title" and delete the other columns
- Add one or more todo items to the table
- Open code editor
- Create a new file named
.env.local
in the root directory
dotenv
files use the syntaxVARIABLE=value
(See official rules)
- Navigate to https://airtable.com/account
- Click "Generate API Key"
- Copy the key from the input field
- Open
.env.local
- Create a new variable named
REACT_APP_AIRTABLE_API_KEY
- Paste the key as its value
- Click "Help" button
- Click "API Documentation" link
- Copy the "Base ID"
- Open
.env.local
- Create a new variable named
REACT_APP_AIRTABLE_BASE_ID
- Paste the ID as its value
- Open
src/App.js
- Inside the first
useEffect
hook, replace the placeholderPromise
with the following... - Using the Fetch API, create a "GET" request and pass the necessary arguments:
- url: the url of your request (in this case,
https://api.airtable.com/v0/${process.env.REACT_APP_AIRTABLE_BASE_ID}/Default
note that we need to useprocess.env...
to get env variables and remember that you need to use back-ticks and a dollar sign to do string formatting (see Using Fetch - Web APIs | MDN (Supplying request options section) for a reminder if needed) - options: the object of additional request options
- url: the url of your request (in this case,
- In the
fetch
options object, add the propertyheaders
as an object with the following property:- Authorization: the token to authorize the request (in this case "Bearer {API_KEY}" where
{API_KEY}
is replaced with the corresponding environment variable)
- Authorization: the token to authorize the request (in this case "Bearer {API_KEY}" where
- Chain a
then
method to your fetch call and pass it a function that returns the response JSON data - Update the
setToDoList
call to reference the new result format (hint:result.records
) - Open
src/TodoListItem.js
- Update the todo item title to reference the new object format (hint:
todo.fields.Title
)
If you'd like to stretch your skills and attempt creating/writing data to AirTable from your app, give it a try! One thing to note before you attempt: make sure your onAddTodo function has the same data structure as the AirTable data so you don't wind up having data added to the wrong column or get an error as a result of mismatched data.