Skip to content

Commit c7d0e1a

Browse files
committed
Merge branch 'master' of github.com:redux-json-api/react-redux-json-api
2 parents e89e40b + ede583f commit c7d0e1a

File tree

1 file changed

+126
-5
lines changed

1 file changed

+126
-5
lines changed

README.md

Lines changed: 126 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Under development
22
=================
33

4-
# redux-json-api-scope
4+
# react-redux-json-api
55

66
- a react component which automatically renders childs depending on returned data
77
- i.e. also maintains a state of particular set of resources
@@ -12,6 +12,9 @@ Under development
1212
- clean up unused resources
1313
- better than connect/thunk/request pattern
1414
- use `Query` as the "container", no need to dispatch actions for API requests
15+
- how to handle ssr?
16+
- refresh original/current request
17+
- load another url
1518

1619
```
1720
<Query>
@@ -23,12 +26,130 @@ Under development
2326

2427
## Usage Example
2528

26-
```js
29+
In the most simple example, you just drop `Query` into your component hierarchy where you need the data. Consider a page that lists all your blog posts, you render a header and footer, and query for the actual posts themselves.
30+
31+
```jsx
2732
import Query from 'react-redux-json-api';
33+
import BlogPost from './BlogPost';
2834

2935
const BlogPosts = () => (
30-
<Query readFrom="/blog-posts">
31-
{({ resources }) => <ul>{resources.map(post => <li>{post.title}</li>)}</ul>}
32-
</Query>
36+
<article>
37+
<header>Hello Blog</header>
38+
<Query endpoint="/blog-posts">
39+
{({ resources }) => resources.map(post => (
40+
<section>
41+
<a href={`/post/${post.id}`}>{post.attributes.title}</a>
42+
</section>
43+
))}
44+
</Query>
45+
<footer>&copy; 2018</footer>
46+
</article>
3347
);
3448
```
49+
50+
### Container/Presenter Pattern
51+
52+
A very popular way to divide responsibilities in React apps is the [container/presenter pattern](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0). This is potentially a very clean pattern to use with our `Query` component if you include this in your container component:
53+
54+
#### `container.js`
55+
56+
```jsx
57+
import Query from 'react-redux-json-api';
58+
import BlogPosts from './presenter';
59+
60+
function Loading() {
61+
return <div>I'm loading</div>;
62+
}
63+
64+
function Container() {
65+
return (
66+
<Query endpoint="/blog-posts">
67+
{({ loading, resources }) => {
68+
if (loading) return <Loading />;
69+
return <BlogPosts posts={resources} />
70+
}}
71+
</Query>
72+
);
73+
}
74+
```
75+
76+
#### `presenter.js`
77+
```jsx
78+
import BlogPost from './BlogPost';
79+
80+
export default function BlogPosts() {
81+
return (
82+
<article>
83+
<header>Hello Blog</header>
84+
{({ posts }) => posts.map(post => (
85+
<section>
86+
<a href={`/post/${post.id}`}>{post.attributes.title}</a>
87+
</section>
88+
))}
89+
<footer>&copy; 2018</footer>
90+
</article>
91+
);
92+
}
93+
```
94+
95+
## Caching
96+
97+
`Query` has the ability to cache the result set of executed requests in memory. The caching strategy is very simple, as the result set is stored in a cache key that corresponds to `links.self` from the json:api body, falling back to the provided `endpoint` if `links.self` is not present.
98+
99+
If you would like to use the caching mechanism it is highly encouraged that `links.self` is a permalink for all cached requests. This is important to avoid overlap or missing resources, see this detailed below. To opt-in set boolean prop `enableCache` on your `Query` instance; `<Query endpoint="..." enableCache />`
100+
101+
### Why Permalinks are Important
102+
103+
Let's use above example with blog posts. When a user initially loads our page, we will request `/blog-posts` to retrieve the latest blog posts. We will also add buttons to go to next and prev pages:
104+
105+
```jsx
106+
<Query endpoint="blog-posts">
107+
({ resources, links }) => (
108+
<>
109+
<BlogPosts posts={resources} />
110+
{links.prev && <button onClick={links.next.load}>Prev</button>}
111+
{links.next && <button onClick={links.next.load}>Next</button>}
112+
</>
113+
)
114+
</Query>
115+
```
116+
117+
_Note: This is a naive implementation_
118+
119+
#### Error Prone Response
120+
121+
This is an example response body for above code example, that will be error prone when cached. Let's say a user is vieweing the first page on your website, meanwhile you're creating a new blog post. When the user loads page 2, they will see the last blog post from page 1, on page 2 as well. Not only is this error prone when cached, but it's particularly bad because the result set from the first page is cached and will not refresh to the user.
122+
123+
```json
124+
GET /blog-posts
125+
126+
{
127+
"links": {
128+
"self": "/blog-posts",
129+
"next": "/blog-posts?page=2"
130+
},
131+
"data": {
132+
"...": {}
133+
}
134+
}
135+
```
136+
137+
#### Good Response
138+
139+
A response that would work well and prevent overlapping resources, uses some form of permalinking to a particular result set:
140+
141+
```json
142+
GET /blog-posts
143+
144+
{
145+
"links": {
146+
"self": "/blog-posts?fromTimestamp=1445412480",
147+
"next": "/blog-posts?fromTimestamp=499165200"
148+
},
149+
"data": {
150+
"...": {}
151+
}
152+
}
153+
```
154+
155+
In this case `links.self` does not reflect the requested URL, instead the server is set up to provide a link that is guaranteed to return the same result set. In this case the strategy is to use the timestamp of the latest blog post. This makes it a far more reliable key for our caching mechanism.

0 commit comments

Comments
 (0)