Skip to content

Commit 89df27e

Browse files
committed
add users list with fetch example and corres. minimal test
1 parent 71a6afb commit 89df27e

File tree

8 files changed

+202
-433
lines changed

8 files changed

+202
-433
lines changed

examples/cookbook/app/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ type Recipe = {
8282
};
8383

8484
const recipes: Recipe[] = [
85-
{ id: 2, title: 'Welcome Screen with Custom Render', path: 'custom-render/' },
86-
{ id: 1, title: 'Task List with Jotai', path: 'jotai/' },
85+
{ id: 1, title: 'Welcome Screen with Custom Render', path: 'custom-render/' },
86+
{ id: 2, title: 'Task List with Jotai', path: 'state-management/jotai/' },
87+
{ id: 3, title: 'Users Home with\na Variety of Net. Req. Methods', path: 'network-requests/' },
8788
];
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { User } from './types';
3+
import UserList from './components/UserList';
4+
5+
export default () => {
6+
const [data, setData] = useState<User[]>([]);
7+
useEffect(() => {
8+
const run = async () => {
9+
const _data = await getUsersWithFetch();
10+
setData(_data);
11+
};
12+
13+
void run();
14+
}, []);
15+
16+
return (
17+
<>
18+
{/*Todo: maybe this should be a phone book app with all contacts, catch up suggestions at the top woth avatar*/}
19+
{/*add phonbe number to user maybe?*/}
20+
{/* TODO: Add Catch up with... UserSuggestionsList with avatar horizontally in top that will utilize axios for example*/}
21+
{/*maybe add one example of react query?*/}
22+
<UserList users={data} />
23+
</>
24+
);
25+
};
26+
27+
const getUsersWithFetch = async (): Promise<User[]> => {
28+
const res = await fetch('https://randomuser.me/api/?results=25');
29+
const json = await res.json();
30+
return json.results;
31+
};
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { render, screen, waitForElementToBeRemoved } from '@testing-library/react-native';
2+
import React from 'react';
3+
import UsersHome from '../UsersHome';
4+
import { User } from '../types';
5+
6+
beforeAll(() => {
7+
jest.spyOn(global, 'fetch').mockImplementation(
8+
jest.fn(() => {
9+
throw Error('Only Chuck Norris is allowed to make API requests when testing ;)');
10+
}),
11+
);
12+
});
13+
14+
afterAll(() => {
15+
(global.fetch as jest.Mock).mockRestore();
16+
});
17+
18+
describe('UsersListFetch', () => {
19+
it('fetches users successfully and renders in list', async () => {
20+
jest.spyOn(global, 'fetch').mockResolvedValueOnce({
21+
json: jest.fn().mockResolvedValueOnce(DATA),
22+
} as unknown as Response);
23+
render(<UsersHome />);
24+
25+
await waitForElementToBeRemoved(() => screen.getByText(/users data not quite there yet/i));
26+
expect(await screen.findByText('Name: Mrs Ida Kristensen')).toBeOnTheScreen();
27+
expect(await screen.findByText('Email: [email protected]')).toBeOnTheScreen();
28+
expect(await screen.findAllByText(/name/i)).toHaveLength(3);
29+
});
30+
});
31+
32+
const DATA: { results: User[] } = {
33+
results: [
34+
{
35+
name: {
36+
title: 'Mrs',
37+
first: 'Ida',
38+
last: 'Kristensen',
39+
},
40+
41+
id: {
42+
name: 'CPR',
43+
value: '250562-5730',
44+
},
45+
picture: {
46+
large: 'https://randomuser.me/api/portraits/women/26.jpg',
47+
medium: 'https://randomuser.me/api/portraits/med/women/26.jpg',
48+
thumbnail: 'https://randomuser.me/api/portraits/thumb/women/26.jpg',
49+
},
50+
},
51+
{
52+
name: {
53+
title: 'Mr',
54+
first: 'Elijah',
55+
last: 'Ellis',
56+
},
57+
58+
id: {
59+
name: 'TFN',
60+
value: '138117486',
61+
},
62+
picture: {
63+
large: 'https://randomuser.me/api/portraits/men/53.jpg',
64+
medium: 'https://randomuser.me/api/portraits/med/men/53.jpg',
65+
thumbnail: 'https://randomuser.me/api/portraits/thumb/men/53.jpg',
66+
},
67+
},
68+
{
69+
name: {
70+
title: 'Mr',
71+
first: 'Miro',
72+
last: 'Halko',
73+
},
74+
75+
id: {
76+
name: 'HETU',
77+
value: 'NaNNA945undefined',
78+
},
79+
picture: {
80+
large: 'https://randomuser.me/api/portraits/men/17.jpg',
81+
medium: 'https://randomuser.me/api/portraits/med/men/17.jpg',
82+
thumbnail: 'https://randomuser.me/api/portraits/thumb/men/17.jpg',
83+
},
84+
},
85+
],
86+
};
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { FlatList, Image, StyleSheet, Text, View } from 'react-native';
2+
import React, { useCallback } from 'react';
3+
import type { ListRenderItem } from '@react-native/virtualized-lists';
4+
import { User } from '../types';
5+
6+
export default ({ users }: { users: User[] }) => {
7+
const renderItem: ListRenderItem<User> = useCallback(
8+
({ item: { name, email, picture }, index }) => {
9+
const { title, first, last } = name;
10+
const backgroundColor = index % 2 === 0 ? '#f9f9f9' : '#fff';
11+
return (
12+
<View style={[{ backgroundColor }, styles.userContainer]}>
13+
<Image source={{ uri: picture.thumbnail }} style={styles.userImage} />
14+
<View>
15+
<Text>
16+
Name: {title} {first} {last}
17+
</Text>
18+
<Text>Email: {email}</Text>
19+
</View>
20+
</View>
21+
);
22+
},
23+
[],
24+
);
25+
26+
if (users.length === 0) return <FullScreenLoader />;
27+
28+
return (
29+
<View>
30+
<FlatList<User>
31+
data={users}
32+
renderItem={renderItem}
33+
keyExtractor={(item, index) => `${index}-${item.id.value}`}
34+
/>
35+
</View>
36+
);
37+
};
38+
const FullScreenLoader = () => {
39+
return (
40+
<View style={styles.loaderContainer}>
41+
<Text>Users data not quite there yet...</Text>
42+
</View>
43+
);
44+
};
45+
46+
const styles = StyleSheet.create({
47+
userContainer: {
48+
padding: 16,
49+
flexDirection: 'row',
50+
alignItems: 'center',
51+
},
52+
userImage: {
53+
width: 50,
54+
height: 50,
55+
borderRadius: 24,
56+
marginRight: 16,
57+
},
58+
loaderContainer: { flex: 1, justifyContent: 'center', alignItems: 'center' },
59+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import * as React from 'react';
2+
import UsersHome from './UsersHome';
3+
4+
export default function Example() {
5+
return <UsersHome />;
6+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export type User = {
2+
name: {
3+
title: string;
4+
first: string;
5+
last: string;
6+
};
7+
email: string;
8+
id: {
9+
name: string;
10+
value: string;
11+
};
12+
picture: {
13+
large: string;
14+
medium: string;
15+
thumbnail: string;
16+
};
17+
};

0 commit comments

Comments
 (0)