Skip to content

Commit 6c7f39c

Browse files
author
Johannes Klauss
committed
Added documentation to readme.
1 parent d2fb03f commit 6c7f39c

File tree

10 files changed

+10527
-14261
lines changed

10 files changed

+10527
-14261
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
pkg/
2+
13
# Logs
24
logs
35
*.log

README.md

Lines changed: 171 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,172 @@
11
# react-use-fetch-factory
2-
A factory pattern for react hooks to fetch data via redux and select via reselect
2+
A factory pattern for react hooks to abstract away your tedious data fetching and selecting
3+
logic.
4+
5+
## Install
6+
```
7+
npm i react-use-fetch-factory
8+
```
9+
or
10+
```
11+
yarn add react-use-fetch-factory
12+
```
13+
14+
## API
15+
The Hook takes three parameters.
16+
* `selector` The selector you are using to retrieve your data from the redux state.
17+
* `fetchActionCreator` An action creator that returns an action to trigger a fetch inside
18+
a saga, thunk, etc.
19+
* `emptyArrayAsFalsy` The Hook will check if there is data in the redux store. If there is
20+
data found, the fetch will not be executed. Only if the selector returns `null` or
21+
`undefined` the fetch action will be triggered. Setting this parameter to `true` will
22+
include an empty array as `falsy` and trigger the fetch action.
23+
24+
## Usage
25+
The best usage is to build your own custom Hook which uses the `useFetchFactory` Hook, like so:
26+
27+
```js
28+
import {useFetchFactory} from 'react-use-fetch-factory';
29+
import {getProducts, fetchProducts} from '../ducks/products';
30+
31+
export default function useProducts(ids) {
32+
return useFetchFactory(
33+
getProducts,
34+
() => fetchProducts(ids),
35+
true
36+
);
37+
}
38+
```
39+
40+
With you custom hook you can then import your selecting and fetching logic with one line:
41+
```jsx harmony
42+
import * as React from 'react';
43+
import {useProducts} from '../hooks/fetching';
44+
45+
export function ProductsList(props) {
46+
const products = useProducts(props.productIds);
47+
48+
return (
49+
<List>
50+
{products.map((product) => {
51+
<ProductView key={product.id} {...product}/>
52+
})}
53+
</List>
54+
);
55+
}
56+
```
57+
58+
Pretty neat, eh?
59+
60+
## Motivation & How it works
61+
Hooks are a great way to make our components leaner. With the release of react-redux 7 we
62+
can finally use Hooks to provide our store to components. Previously we had to use `connect` to
63+
bind our components to the redux store which ended in a complicated boilerplate looking higher
64+
order component. This became particular annoying when dealing with selectors and data from
65+
the server, because all of this check, fetching and selecting usually happened inside the component
66+
code:
67+
68+
```jsx harmony
69+
import * as React from 'react';
70+
import {connect} from 'react-redux';
71+
import {fetchTodos, toggleTodo, getTodosSelector} from '../ducks/todos';
72+
73+
class TodoApp extends React.Component {
74+
componentDidMount() {
75+
if(this.props.todos.length === 0) {
76+
this.props.fetchTodos();
77+
}
78+
}
79+
80+
render() {
81+
return (
82+
<List>
83+
{this.props.todos.map((todo) => {
84+
<Todo key={todo.id} onToggle={() => toggleTodo(todo.id)}/>
85+
})}
86+
</List>
87+
);
88+
}
89+
}
90+
91+
function mapStateToProps(state) {
92+
return {
93+
todos: getTodosSelector(state)
94+
}
95+
}
96+
97+
function mapDispatchToProps() {
98+
return {
99+
fetchTodos,
100+
addTodo,
101+
toggleTodo,
102+
}
103+
}
104+
105+
export default connect(
106+
mapStateToProps,
107+
mapDispatchToProps
108+
)(TodoApp)
109+
```
110+
111+
This is a lot of code for just displaying a list of Todos. We want to check if there are
112+
todos when the component mounts and if not, we fetch them (e.g. via a saga, thunk or whatever you like)
113+
And not only is this a lot of code, but we'll also repeat ourselves over and over again the
114+
more components we have that just fetch some kind of data and display it.
115+
116+
With hooks we can get leaner:
117+
118+
```jsx harmony
119+
import React, {useEffect} from 'react';
120+
import {useDispatch, useSelector} from 'react-redux';
121+
import {fetchTodos, toggleTodo, getTodosSelector} from '../ducks/todos';
122+
123+
export function TodoApp() {
124+
const dispatch = useDispatch();
125+
const todos = useSelector(getTodosSelector);
126+
127+
useEffect(() => {
128+
if (todos.length === 0) {
129+
dispatch(fetchTodos());
130+
}
131+
}, [dispatch, fetchTodos]);
132+
133+
return (
134+
<List>
135+
{todos.map((todo) => {
136+
<Todo key={todo.id} onToggle={() => dispatch(toggleTodo(todo.id))}/>
137+
})}
138+
</List>
139+
);
140+
}
141+
```
142+
143+
Way cleaner. But we also will repeat ourselves in this case. The only thing that will change
144+
if we fetch different data is the selector, the dispatched fetching action and the ui
145+
representation.
146+
But wait. We can build our own Hooks! So let's abstract all this away:
147+
148+
```jsx harmony
149+
import {useEffect} from 'react';
150+
import {useDispatch, useSelector} from 'react-redux';
151+
152+
export default function useFetchFactory(selector, fetchActionCreator, emptyArrayAsFalsy) {
153+
const dispatch = useDispatch();
154+
const data = useSelector(selector);
155+
156+
useEffect(() => {
157+
if (
158+
!data ||
159+
(emptyArrayAsFalsy && data instanceof Array && data.length === 0)
160+
) {
161+
dispatch(fetchActionCreator());
162+
}
163+
}, [dispatch]);
164+
165+
return data;
166+
}
167+
```
168+
169+
And there it is! Our own custom data fetching and selecting Hook!
170+
171+
## Found an issue or bug?
172+
Please open a pull request or an issue and contribute.

docs/intro.mdx

Whitespace-only changes.

docs/useFetchFactory.mdx

Whitespace-only changes.

doczrc.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import pkg from './package.json';
2+
3+
export default {
4+
title: 'React Hotkeys Hook',
5+
description: pkg.description,
6+
base: `/${pkg.name}/`,
7+
version: pkg.version,
8+
propsParser: false,
9+
hashRouter: true,
10+
typescript: true,
11+
themeConfig: {
12+
colors: {
13+
primary: '#000000',
14+
},
15+
},
16+
};

0 commit comments

Comments
 (0)