Skip to content

Commit aaba5c9

Browse files
committed
add docs for hacker-news-api example
1 parent 7b4c5b0 commit aaba5c9

File tree

2 files changed

+72
-5
lines changed

2 files changed

+72
-5
lines changed

docs/examples/hacker-news-api.md

+71
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,77 @@ We will use official [HN API](https://github.com/HackerNews/API), which is hoste
66
- [Firebase](https://firebase.google.com/)
77
- [Web setup](https://firebase.google.com/docs/web/setup)
88

9+
We will start downloading list of stories by type. Hackernews API returns just an array of the size 500, we will add pagination to it later:
10+
11+
```javascript
12+
export const storiesTile = createTile({
13+
type: ['hn_api', 'stories'],
14+
fn: ({ api, params }) => api.child(params.type).once('value').then(snapshot => snapshot.val()),
15+
nesting: ({ type }) => [type],
16+
caching: true,
17+
});
18+
```
19+
20+
Despite using Firebase, here we don't really want to do real-time updates, just for the sake of simplicity, so we don't subscribe or do something like that. We get 500 items for given type – so we can download `topstories`, `new` and other categories using just this tile. We also cache results, so we can declaratively invoke it each time we need just a page, and be sure that it won't be downloaded again.
21+
22+
Now let's create tile for a single item. It will just download a single item, placing it under id namespace:
23+
24+
```javascript
25+
export const itemTile = createTile({
26+
type: ['hn_api', 'item'],
27+
fn: ({ api, params }) => api.child(`item/${params.id}`).once('value').then(snapshot => snapshot.val()) ,
28+
nesting: ({ id }) => [id],
29+
caching: true,
30+
});
31+
```
32+
33+
As you can see, the implementation is extremely similar to the previous item, `storiesTile`. The thing is that they are performing almost the same operation – request some API call, parse results (if needed – here we don't have to), set up nesting and caching – and, in fact, this is how it is supposed to be. The idea behind `redux-tiles` is that because of boilerplate-free code it is very cheap to create small "tiles", which represent atomic piece of functionality, and then combine them later.
34+
Let's create tile for downloading list of items – there is no such endpoint, so we will have to compose existing tile:
35+
36+
```javascript
37+
export const itemsTile = createTile({
38+
type: ['hn_api', 'items'],
39+
fn: ({ dispatch, actions, params }) =>
40+
Promise.all(params.ids.map(id =>
41+
dispatch(actions.hn_api.item({ id }))
42+
))
43+
});
44+
```
45+
46+
We just iterate over ids and request all ids inside. If we want to perform only certain amount of simulatenous requests, we can chunkify these requests here (but for other tiles it will be completely abstracted).
47+
48+
And finally, now we can create functionality for returning stories by type with pagination. The logic is the following:
49+
- get list of all stories for this type
50+
- calculate ids for given page
51+
- download all items for these ids
52+
- nest them into `[type, pageNumber, pageSize]`
53+
54+
```javascript
55+
export const itemsByPageTile = createTile({
56+
type: ['hn_api', 'pages'],
57+
fn: async ({ params: { type = 'topstories', pageNumber = 0, pageSize = 30 }, selectors, getState, actions, dispatch }) => {
58+
// we can always fetch stories, they are cached, so if this type
59+
// was already fetched, there will be no new request
60+
await dispatch(actions.hn_api.stories({ type }));
61+
const { data } = selectors.hn_api.stories(getState(), { type });
62+
const offset = pageNumber * pageSize;
63+
const end = offset + pageSize;
64+
const ids = data.slice(offset, end);
65+
66+
// download list of ids for given page
67+
await dispatch(actions.hn_api.items({ ids }));
68+
69+
// populate ids with real values
70+
return ids.map(id => selectors.hn_api.item(getState(), { id }).data);
71+
},
72+
// we can safely nest them this way, and be sure that individual items will be cached
73+
// so, changing number of items on the page might not even require a single new request
74+
nesting: ({ type = 'topstories', pageNumber = 0, pageSize = 50 }) => [type, pageSize, pageNumber],
75+
});
76+
```
77+
78+
The last tile contains main business logic for our application, but it does not contain any direct api request, so if in the future response for some endpoint will change, or we will have to do different requests to get the same data, we can change it only inside these small tiles (parsing data or dispatching other small tiles).
79+
980
## Links to implementation
1081

1182
- [complete code](https://github.com/Bloomca/redux-tiles/tree/master/examples/hacker-news-api)

examples/hacker-news-api/hn-tiles.js

+1-5
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,7 @@ export const itemsByPageTile = createTile({
3939
const end = offset + pageSize;
4040
const ids = data.slice(offset, end);
4141
await dispatch(actions.hn_api.items({ ids }));
42-
return ids.map(id => {
43-
const data = selectors.hn_api.item(getState(), { id });
44-
45-
return data;
46-
});
42+
return ids.map(id => selectors.hn_api.item(getState(), { id }).data);
4743
},
4844
// we can safely nest them this way, and be sure that individual items will be cached
4945
// so, changing number of items on the page might not even require a single new request

0 commit comments

Comments
 (0)