4.2 Reading Tasks
It's time to query our API for all pending and completed tasks and display them as a list.
Step 1: Task List Item
The first step is to create a list item that will render a single task.
Let's create a new component called TaskListItem
. This component will have 2 states:
Loading State: When data is loading from the database. The modern way is to show a placeholder component. To achieve this, we use the
@bluebase/plugin-rn-placeholder
plugin. This will add the components from the rn-placeholder library in BlueBase. Make sure you install and add this plugin to your project.Data State: This will render the task data after it is loaded from the database. For this, we will use
List.Item
component.
See the following code for a reference implementation:
import { Checkbox, List } from '@bluebase/components';
import { getComponent, useNavigation } from '@bluebase/core';
import { PlaceholderListItemProps } from '@bluebase/plugin-rn-placeholder';
import React, { useCallback } from 'react';
import { Tasks } from '../../graphql-types';
const PlaceholderListItem = getComponent<PlaceholderListItemProps>('PlaceholderListItem');
export interface TaskListItemProps extends Tasks {
loading?: boolean;
}
export const TaskListItem = (props: TaskListItemProps) => {
const {
id,
title,
description,
completed,
loading,
} = props;
if (loading === true) {
return <PlaceholderListItem avatar description variant="icon" />;
}
const { push } = useNavigation();
const onPress = useCallback(() => {
push('EditTask', { taskId: id });
}, [id]);
return (
<List.Item
title={title}
description={description}
left={<Checkbox checked={!!completed} disabled />}
onPress={onPress}
/>
);
};
TaskListItem.defaultProps = {};
TaskListItem.displayName = 'TaskListItem';
export * from './TaskListItem';
import { TaskListItem } from './TaskListItem';
export default TaskListItem;
Step 2: Task List Empty State
We also need to create a component that will be rendered when a list is empty. This component will show an empty message, as well as a call to action button to create a new task:
import { ComponentState, ComponentStateProps } from '@bluebase/components';
import { useNavigation } from '@bluebase/core';
import React, { useCallback } from 'react';
export interface TaskListEmptyStateProps extends ComponentStateProps {}
export const TaskListEmptyState = (props: TaskListEmptyStateProps) => {
const { navigate } = useNavigation();
const goToCreate = useCallback(() => navigate('CreateTask'), []);
return (
<ComponentState
title="No tasks"
description="Start by creating a new task"
actionTitle="Create Task"
imageProps={{ resizeMode: 'contain' }}
actionOnPress={goToCreate}
actionProps={{ size: 'small', color: 'success', variant: 'outlined' }}
{...props}
/>
);
};
TaskListEmptyState.displayName = 'TaskListEmptyState';
export * from './TaskListEmptyState';
import { TaskListEmptyState } from './TaskListEmptyState';
export default TaskListEmptyState;
Step 3: GraphQL Query
Now with these views out of the way, it's time to start creating the actual list. The first step is to write our GraphQL query.
We intend to create our list with infinite scrolling enabled. So, whenever new data is loaded, we need to merge it with the existing data, to show it as one list. To achieve this, we also create a function in this file called TasksCollectionQueryUpdateQueryFn
.
import { FetchMoreOptions } from '@apollo/client';
import gql from 'graphql-tag';
import { TasksCollectionQueryQuery } from '../../graphql-types';
export const TasksCollectionQuery = gql`
query TasksCollectionQuery(
$filter: tasksFilter
$first: Int
# $last: Int
# $before: Cursor
$after: Cursor
) {
tasksCollection(
filter: $filter
first: $first
# last: $last
# before: $before
after: $after
) {
edges {
cursor
node {
id
title
completed
}
}
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
}
}
`;
export const TasksCollectionQueryUpdateQueryFn: FetchMoreOptions<TasksCollectionQueryQuery>['updateQuery'] = (
previousResult,
{ fetchMoreResult }
) => {
if (!fetchMoreResult) {
return previousResult;
}
const prevEdges = previousResult.tasksCollection?.edges || [];
const newEdges = fetchMoreResult.tasksCollection?.edges || [];
return {
// Put the new items at the end of the list and update `pageInfo`
// so we have the new `endCursor` and `hasNextPage` values
tasksCollection: {
...previousResult.tasksCollection,
edges: [...prevEdges, ...newEdges],
pageInfo: {
...previousResult.tasksCollection?.pageInfo,
endCursor: fetchMoreResult.tasksCollection?.pageInfo?.endCursor,
hasNextPage: !!fetchMoreResult.tasksCollection?.pageInfo?.hasNextPage,
hasPreviousPage: !!fetchMoreResult.tasksCollection?.pageInfo?.hasPreviousPage,
startCursor: fetchMoreResult.tasksCollection?.pageInfo?.startCursor,
},
},
};
};
There is a bug in Supabase that doesn't let us send "last" and "before" params. When it is fixed, these lines should be uncommented.
Step 4: Generate Typings
We will now generate typescript interfaces for our GraphQL API. Execute the following command:
yarn graphql-codegen
This will update the file src/graphql-types.ts
.
Step 5: Task List
With all the prep work now complete, let's finally create our list. For this we will use the GraphqlList
component from the @bluebase/plugin-json-graphql-components
plugin. This component takes care of all the heavy lifting.
We create this component with a complete
prop so that we can filter task based on this value. This will be passed as a variable to the GraphQL query.
import { QueryResult } from '@apollo/client';
import { Divider } from '@bluebase/components';
import { getComponent } from '@bluebase/core';
import { GraphqlConnection, GraphqlListProps } from '@bluebase/plugin-json-graphql-components';
import React from 'react';
import { ListRenderItemInfo } from 'react-native';
import { Tasks, TasksCollectionQueryQuery } from '../../graphql-types';
import TaskListEmptyState from '../TaskListEmptyState';
import { TaskListItem, TaskListItemProps } from '../TaskListItem';
import { TasksCollectionQuery, TasksCollectionQueryUpdateQueryFn } from './TasksCollectionQuery.graphql';
const GraphqlList = getComponent<GraphqlListProps<TaskListItemProps, TasksCollectionQueryQuery>>('GraphqlList');
function mapQueryResultToConnection(result: QueryResult<TasksCollectionQueryQuery>) {
return result.data?.tasksCollection as GraphqlConnection<Tasks>;
}
function renderItem({ item }: ListRenderItemInfo<TaskListItemProps>) {
return <TaskListItem {...item} />;
}
const renderDivider = () => <Divider inset />;
export interface TaskListProps {
completed: boolean;
}
export const TaskList = (props: TaskListProps) => {
const { completed } = props;
const itemsPerPage = 10;
return (
<GraphqlList
key="task-list"
pagination="infinite"
itemsPerPage={itemsPerPage}
query={TasksCollectionQuery}
updateQueryInfinitePagination={TasksCollectionQueryUpdateQueryFn}
mapQueryResultToConnection={mapQueryResultToConnection}
renderItem={renderItem}
ItemSeparatorComponent={renderDivider}
ListEmptyComponent={TaskListEmptyState}
queryOptions={{
variables: {
filter:{ completed: { eq: completed } }
}
}}
/>
);
};
TaskList.displayName = 'TaskList';
Create the index file:
export * from './TaskList';
import { TaskList } from './TaskList';
export default TaskList;
Step 6: Update screens
Last but not the least, lets add this list to the PendingTasksScreen
and CompletedTasksScreen
screens:
import React from 'react';
import { TaskList } from '../../components/TaskList';
export const PendingTasksScreen = () => {
return (
<TaskList completed={false} />
);
};
PendingTasksScreen.displayName = 'PendingTasksScreen';
import React from 'react';
import TaskList from '../../components/TaskList';
export const CompletedTasksScreen = () => {
return (
<TaskList completed />
);
};
CompletedTasksScreen.displayName = 'CompletedTasksScreen';
Refresh the app, and see it come to life. Enjoy!
Loading State


Empty State


Data State


Last updated
Was this helpful?