# 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.&#x20;

Let's create a new component called `TaskListItem` . This component will have 2 states:

1. **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](https://github.com/mfrachet/rn-placeholder) library in BlueBase. Make sure you install and add this plugin to your project.
2. **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:

{% code title="src/components/TaskListItem/TaskListItem.tsx" %}

```typescript
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';
```

{% endcode %}

{% code title="src/components/TaskListItem/index.ts" %}

```typescript
export * from './TaskListItem';

import { TaskListItem } from './TaskListItem';
export default TaskListItem;
```

{% endcode %}

### 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:

{% code title="src/components/TaskListEmptyState/TaskListEmptyState.tsx" %}

```typescript
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';
```

{% endcode %}

{% code title="src/components/TaskListEmptyState/index.ts" %}

```typescript
export * from './TaskListEmptyState';

import { TaskListEmptyState } from './TaskListEmptyState';
export default TaskListEmptyState;
```

{% endcode %}

### 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` .

{% code title="src/components/TaskList/TasksCollectionQuery.graphql.ts" %}

```typescript
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,
			},
		},
	};
};
```

{% endcode %}

{% hint style="danger" %}
There is a [bug](https://github.com/supabase/pg_graphql/issues/161) in Supabase that doesn't let us send "last" and "before" params. When it is fixed, these lines should be uncommented.
{% endhint %}

### 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.&#x20;

{% code title="src/components/TaskList/TaskList.tsx" %}

```typescript
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';
```

{% endcode %}

Create the index file:

{% code title="src/components/TaskList/index.ts" %}

```typescript
export * from './TaskList';

import { TaskList } from './TaskList';
export default TaskList;
```

{% endcode %}

### Step 6: Update screens

Last but not the least, lets add this list to the `PendingTasksScreen` and `CompletedTasksScreen` screens:

{% code title="src/screens/PendingTasksScreen/PendingTasksScreen.tsx" %}

```typescript
import React from 'react';

import { TaskList } from '../../components/TaskList';

export const PendingTasksScreen = () => {
	return (
		<TaskList completed={false} />
	);
};

PendingTasksScreen.displayName = 'PendingTasksScreen';
```

{% endcode %}

{% code title="src/screens/CompletedTasksScreen/CompletedTasksScreen.tsx" %}

```typescript
import React from 'react';

import TaskList from '../../components/TaskList';

export const CompletedTasksScreen = () => {
	return (
		<TaskList completed />
	);
};

CompletedTasksScreen.displayName = 'CompletedTasksScreen';
```

{% endcode %}

Refresh the app, and see it come to life. Enjoy!

#### Loading State

<div align="right"><img src="https://3369392052-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LPjsIbS1DfhqT8BS3Ui%2Fuploads%2FIbPlXRLcsCt1e9UxdLnu%2FScreenshot%202022-04-23%20at%2011.31.15%20PM.png?alt=media&#x26;token=039715be-d25b-47ad-b4e5-a0b1a3403d82" alt="Loading State on Web"> <img src="https://3369392052-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LPjsIbS1DfhqT8BS3Ui%2Fuploads%2FNMr5G60nbUkFpCcSKRbz%2FScreenshot%202022-04-23%20at%2011.30.58%20PM.png?alt=media&#x26;token=9410dac6-ca64-4e77-b4c7-0c81732cea87" alt="Loading State on iOS"></div>

#### Empty State

![Empty State on Web](https://3369392052-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LPjsIbS1DfhqT8BS3Ui%2Fuploads%2FrorImV4YVJlVxKdOGBOb%2FScreenshot%202022-04-23%20at%204.45.12%20PM.png?alt=media\&token=eb34c08c-ec3d-4bb7-a55a-43b5300fe53a) ![Empty State on iOS](https://3369392052-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LPjsIbS1DfhqT8BS3Ui%2Fuploads%2FTDOOBhOYAK8rMJxMmPxK%2FScreenshot%202022-04-23%20at%204.45.32%20PM.png?alt=media\&token=8550e653-25e0-4014-a2e7-9bedb4d5175d)

#### Data State

![Data State on Web](https://3369392052-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LPjsIbS1DfhqT8BS3Ui%2Fuploads%2FVNcN6csbZUqYtI4ZtZT6%2FScreenshot%202022-04-23%20at%204.45.27%20PM.png?alt=media\&token=a4843aa6-8773-4be0-8124-b8cdb9427cd5) ![Data State on iOS](https://3369392052-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LPjsIbS1DfhqT8BS3Ui%2Fuploads%2FeUJ24Olc0u0svpEBjp29%2FScreenshot%202022-04-23%20at%204.45.54%20PM.png?alt=media\&token=714953f7-82b4-48a7-86f3-4c325f43334a)
