|
1 |
| -Grapher-React Components |
2 |
| -======================== |
| 1 | +## Grapher React Components |
3 | 2 |
|
4 | 3 | Using the [cultofcoders:grapher](https://github.com/cult-of-coders/grapher) query component in React.
|
5 | 4 |
|
6 |
| -createQueryContainer |
7 |
| --------------------- |
| 5 | +### Installation |
| 6 | +```bash |
| 7 | +meteor add cultofcoders:grapher-react |
| 8 | +``` |
| 9 | + |
| 10 | + |
| 11 | +### Signature |
| 12 | + |
8 | 13 | ```js
|
9 |
| -// TaskList.jsx |
10 |
| -import React from 'react'; |
11 |
| -import Tasks from '/imports/api/tasks/collection.js'; |
12 |
| -import { createQueryContainer } from 'meteor/cultofcoders:grapher-react'; |
| 14 | +withQuery(() => query, config)(Component) |
| 15 | +``` |
| 16 | + |
| 17 | +The first function needs to return a valid Query or NamedQuery from Grapher. |
13 | 18 |
|
14 |
| -const query = Tasks.createQuery({ |
15 |
| - title: 1, |
16 |
| -}); |
| 19 | +### Configuration: |
17 | 20 |
|
18 |
| -const TaskList = ({loading, error, tasks}) => ( |
19 |
| - <div> |
20 |
| - { |
21 |
| - _.map(tasks, task => <div key={task._id}>{task.title}</div>) |
| 21 | +<table> |
| 22 | + <tr> |
| 23 | + <th>Property</th> |
| 24 | + <th>Valid values</th> |
| 25 | + <th>Description</th> |
| 26 | + </tr> |
| 27 | + <tr> |
| 28 | + <td>reactive</td> |
| 29 | + <td>true/false</td> |
| 30 | + <td> |
| 31 | + Defaults to `false`. |
| 32 | + Makes your query reactive (subscribes to changes) or non-reactive, falls back to method calls. |
| 33 | + </td> |
| 34 | + </tr> |
| 35 | + <tr> |
| 36 | + <td>errorComponent</td> |
| 37 | + <td>React.Component (optional)</td> |
| 38 | + <td>Defaults to `null`. Receives `error` object as a prop. Is rendered when subscription or method call triggered an exception</td> |
| 39 | + </tr> |
| 40 | + <tr> |
| 41 | + <td>loadingComponent</td> |
| 42 | + <td>React.Component (optional)</td> |
| 43 | + <td>Defaults to `null`. Renders when the data is waiting to be loaded from the server</td> |
| 44 | + </tr> |
| 45 | + <tr> |
| 46 | + <td>single</td> |
| 47 | + <td>true/false</td> |
| 48 | + <td>Defaults to `false`. If your query is for a single result, then using `true` will send data as an object instead of an array</td> |
| 49 | + </tr> |
| 50 | +</table> |
| 51 | + |
| 52 | +### Simple Usage |
| 53 | + |
| 54 | +```jsx harmony |
| 55 | +import React from 'react'; |
| 56 | +import {withQuery} from 'meteor/cultofcoders:grapher-react'; |
| 57 | + |
| 58 | +const PostList = ({data, isLoading, error}) => { |
| 59 | + if (isLoading) { |
| 60 | + return <div>Loading</div> |
| 61 | + } |
| 62 | + |
| 63 | + if (error) { |
| 64 | + return <div>{error.reason}</div> |
22 | 65 | }
|
23 |
| - </div> |
24 |
| -); |
| 66 | + |
| 67 | + return ( |
| 68 | + <div> |
| 69 | + {data.map(post => <li key={post._id}>{post.title}</li>)} |
| 70 | + </div> |
| 71 | + ) |
| 72 | +} |
25 | 73 |
|
26 |
| -export default createQueryContainer(query, TaskList, { |
27 |
| - reactive: true, // defaults to false, will use pub/sub system |
28 |
| - dataProp: 'tasks', // defaults to 'data' |
29 |
| - single: false, // defaults to false, when you expect a single document, like you filter by _id, use this. |
30 |
| -}); |
| 74 | +export default withQuery((props) => { |
| 75 | + return getPostLists.clone(); |
| 76 | +})(PostList) |
31 | 77 | ```
|
32 | 78 |
|
33 |
| -You can pass params directly in the constructor, these params will be passed to the query. |
| 79 | +### Props Received |
34 | 80 |
|
35 |
| -```js |
36 |
| -import TaskList from './Tasks.jsx'; |
| 81 | +Below are the properties received by the component we wrap, in the example above, that's `PostList` |
| 82 | + |
| 83 | +<table> |
| 84 | + <tr> |
| 85 | + <th>Property</th> |
| 86 | + <th>Valid values</th> |
| 87 | + <th>Description</th> |
| 88 | + </tr> |
| 89 | + <tr> |
| 90 | + <td>isLoading</td> |
| 91 | + <td>true/false</td> |
| 92 | + <td> |
| 93 | + Lets your component know whether the data is waiting to be loaded. |
| 94 | + </td> |
| 95 | + </tr> |
| 96 | + <tr> |
| 97 | + <td>error</td> |
| 98 | + <td>Meteor.Error</td> |
| 99 | + <td>Represents the error triggered from your method or publication. Is falsy if it's not the case.</td> |
| 100 | + </tr> |
| 101 | + <tr> |
| 102 | + <td>refetch</td> |
| 103 | + <td>Function</td> |
| 104 | + <td>For non-reactive queries it passes a refetch function for convenience to help you easily reload the data.</td> |
| 105 | + </tr> |
| 106 | + <tr> |
| 107 | + <td>query</td> |
| 108 | + <td>Query/NamedQuery</td> |
| 109 | + <td>For your convenience, if you ever need the query for any reason, it's passed in there so you can access it.</td> |
| 110 | + </tr> |
| 111 | + <tr> |
| 112 | + <td>...props</td> |
| 113 | + <td></td> |
| 114 | + <td>The props you pass to withQuery, are passed down to the component it wraps</td> |
| 115 | + </tr> |
| 116 | +</table> |
| 117 | + |
| 118 | +### Let's react! |
| 119 | + |
| 120 | +The first example uses the query non-reactively (because that is the default). But let's say you want your query to be reactive (react to changes in the database) |
| 121 | + |
| 122 | +```jsx harmony |
| 123 | +// ... |
| 124 | +export default withQuery((props) => { |
| 125 | + return getPostLists.clone(); |
| 126 | +}, {reactive: true})(PostList) |
| 127 | +``` |
37 | 128 |
|
38 |
| -export default () => { |
39 |
| - return <TaskList params={{isActive: true}} anyOtherProp="willBePassedToComponent" /> |
| 129 | +As mentioned above, the props received are passed down to the component we wrap, meaning: |
| 130 | + |
| 131 | +```jsx harmony |
| 132 | +const PostList = ({data, something}) => { |
| 133 | + return <div>Something is true!</div> |
| 134 | +}; |
| 135 | + |
| 136 | +const Container = withQuery((props) => { |
| 137 | + return getPostLists.clone(); |
| 138 | +}, {reactive: true})(PostList); |
| 139 | + |
| 140 | +export default function () { |
| 141 | + return <Container something={true} />; |
40 | 142 | }
|
41 | 143 | ```
|
| 144 | + |
| 145 | + |
| 146 | +The query object is also passed down as a prop, so, if you ever need it you can access it from there. |
| 147 | + |
| 148 | +For a non-reactive query, we also pass `refetch` function as prop, which simply refetches the query from the database, |
| 149 | +and updates the components properly: |
| 150 | + |
| 151 | +```jsx harmony |
| 152 | +import React from 'react'; |
| 153 | +import {withQuery} from 'meteor/cultofcoders:grapher-react'; |
| 154 | + |
| 155 | +const PostList = ({data, isLoading, error, refetch}) => { |
| 156 | + return ( |
| 157 | + <div> |
| 158 | + <a onClick={refetch}>Reload the data</a> |
| 159 | + {/* Rest of the component */} |
| 160 | + </div> |
| 161 | + ) |
| 162 | +} |
| 163 | + |
| 164 | +export default withQuery((props) => { |
| 165 | + return getPostLists.clone(); |
| 166 | +}, {reactive: false})(PostList) |
| 167 | +``` |
| 168 | + |
| 169 | +If you container wraps a single object, and not a list of objects, you can configure your query like this: |
| 170 | + |
| 171 | +```jsx harmony |
| 172 | +const UserProfile = ({data, isLoading, error}) => { |
| 173 | + return ( |
| 174 | + <div>{data.email}</div> |
| 175 | + ) |
| 176 | +}; |
| 177 | + |
| 178 | +export default withQuery((props) => { |
| 179 | + return getUserProfile.clone({userId: props.userId}); |
| 180 | +}, { |
| 181 | + single: true |
| 182 | +})(UserProfile) |
| 183 | +``` |
| 184 | + |
| 185 | +You will find yourself repeating the same code over and over again for when the query isLoading or it errored. For this you can do: |
| 186 | +```jsx harmony |
| 187 | +function ErrorComponent({error}) { |
| 188 | + return <div>{error.reason}</div> |
| 189 | +}; |
| 190 | + |
| 191 | +function LoadingComponent() { |
| 192 | + return <div>Please wait...</div> |
| 193 | +}; |
| 194 | + |
| 195 | +const UserProfile = ({data}) => { |
| 196 | + return ( |
| 197 | + <div>{data.email}</div> |
| 198 | + ) |
| 199 | +} |
| 200 | + |
| 201 | +export default withQuery((props) => { |
| 202 | + return getUserProfile.clone({userId: props.userId}); |
| 203 | +}, { |
| 204 | + single: true, |
| 205 | + errorComponent: ErrorComponent, |
| 206 | + loadingComponent: LoadingComponent |
| 207 | +})(UserProfile) |
| 208 | +``` |
| 209 | + |
| 210 | +The `UserProfile` component will not render if it's loading or it errored. |
| 211 | + |
| 212 | +To make things even more simple, you can globally define these rules, and all the components by default will have those options. |
| 213 | + |
| 214 | +```jsx harmony |
| 215 | +import {setDefaults} from 'meteor/cultofcoders:grapher-react'; |
| 216 | + |
| 217 | +setDefaults({ |
| 218 | + reactive: false, // you can default it to true |
| 219 | + single: false, // doesn't make sense to default this to true |
| 220 | + errorComponent: ErrorComponent, |
| 221 | + loadingComponent: LoadingComponent |
| 222 | +}) |
| 223 | +``` |
| 224 | + |
| 225 | +If you need custom behavior for a specific component for `error` and `loading` components you can simply do: |
| 226 | + |
| 227 | +```jsx harmony |
| 228 | +export default withQuery((props) => { |
| 229 | + return getUserProfile.clone({userId: props.userId}); |
| 230 | +}, { |
| 231 | + errorComponent: null, |
| 232 | + loadingComponent: AnotherLoadingComponent, |
| 233 | +})(UserProfile) |
| 234 | +``` |
0 commit comments