Skip to content
This repository has been archived by the owner on Dec 23, 2019. It is now read-only.

Commit

Permalink
Refactor goToPage to applyParams
Browse files Browse the repository at this point in the history
Had to switch to lifecycle methods because of
facebook/react#1740 (or so I think)
  • Loading branch information
cvburgess committed Dec 5, 2017
1 parent 47d6cc2 commit 9d72170
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 45 deletions.
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ The `data` prop will have one entry for each action you pass it, and that entry
- `loading: Boolean` - this is `true` while the data is being fetched. once it is returned or an error is thrown, the value will update to `false`
- `error: Object` - this Axios error object is returned if Axios throws an exception (404, 500, ERRCON, etc)
- `refetch(): Function` - function that refetches the data for the given entry
- `goToPage(page: Number): Function` - function that allows paginated resources to change pages
- `applyParmas(params: Object): Function` - function that allows a resource to be sorted, filtered, paginated, etc.
- If the API request is successful, the response is spread into the entry for the action.

Continuing with the example from above:
Expand Down Expand Up @@ -144,23 +144,41 @@ const Notifications = ({ data: { notifications } }) => (
);
```

### Pagination via goToPage()
### Sorting, filtering and paginating data via applyParams()

Every entry in the `data` prop has a `goToPage()` function added to it. This function accepts one parameter (page: Number)
Every entry in the `data` prop has a `applyParams()` function added to it. This function accepts one parameter (params: Object)

```js
const Notifications = ({ data: { notifications } }) => {
const nextPage = (notifications.page_number || 0) + 1;
return (
<div>
<button onClick={() => notifications.goToPage(nextPage)}>Load more</Button>
<button onClick={() => notifications.applyParams({ page: nextPage })}>Load more</Button>
</div>
);
```
```js
const Notifications = ({ data: { notifications } }) => {
return (
<div>
<button onClick={() => notifications.applyParams({ orderby: 'priorty' })}>Load more</Button>
</div>
);
```
```js
const Notifications = ({ data: { notifications } }) => {
return (
<div>
<button onClick={() => notifications.applyParams({ filter: 'foo' })}>Load more</Button>
</div>
);
```
### Reloading data via refetch()
If at any point you want to re-request the data from the server, the `refetch()` function can be used to re-execute the data fetch. This will not reset pagination, instead redoing exactly the same query that was originally executed.
If at any point you want to re-request the data from the server, the `refetch()` function can be used to re-execute the data fetch. This will not reset pagination or other params, instead redoing exactly the same query that was last executed.
```js
const Notifications = ({ data: { notifications } }) => (
Expand Down
72 changes: 47 additions & 25 deletions lib/withData.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,47 +42,67 @@ function withData(actions) {
function _class(props) {
_classCallCheck(this, _class);

// Set all queries to { loading: true, refetch: getData() }
// Set all queries to { loading: true, applyParams: fn(), refetch: fn() }
var _this = _possibleConstructorReturn(this, (_class.__proto__ || Object.getPrototypeOf(_class)).call(this, props));

_initialiseProps.call(_this);

var data = {};
var appliedParams = {};
var actionNames = Object.keys(actions);
actionNames.forEach(function (actionName) {
data[actionName] = {
loading: true,
goToPage: function goToPage(page) {
return _this.goToPage(actionName, page);
applyParams: function applyParams(params) {
return _this.applyParams(actionName, params);
},
refetch: function refetch(params) {
return _this.getData(actionName, _extends({}, params, { noCache: true }));
refetch: function refetch() {
return _this.getData(actionName, { noCache: true });
}
};
appliedParams[actionName] = {};
});
_this.state = {
data: data,
polls: [],
observeIds: []
data: data, // Data object to send to child
appliedParams: appliedParams, // Filters, pages, sorts, etc (matches the structure of `data`)
polls: [], // Ids for polling - stop on unmount
observeIds: [] // Ids for store.js observables - unobserve on unmount
};
return _this;
}

// Set data[actionName] from { loading: false } + the result of the request
_createClass(_class, [{
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps, prevState) {
var _this2 = this;

// This code exists because the setState callback would not work for applyParams below
// May be related to https://github.com/facebook/react/issues/1740
var hasNewParams = prevState.appliedParams !== this.state.appliedParams;
if (hasNewParams) {
var actionNames = Object.keys(this.state.appliedParams);
actionNames.forEach(function (actionName) {
if (prevState.appliedParams[actionName] !== _this2.state.appliedParams[actionName]) {
_this2.getData(actionName);
}
});
}
}

// Set data[actionName] from { loading: false } + the result of the request

// Set data[actionName] from { loading: false } + the { error } generated by Axios

// Set data[actionName] from { loading: false } + the { error } generated by Axios

_createClass(_class, [{
}, {
key: "componentDidMount",
value: function componentDidMount() {
var _this2 = this;
var _this3 = this;

// Request data for each query
var actionNames = Object.keys(actions);
actionNames.forEach(function (actionName) {
return _this2.getData(actionName);
return _this3.getData(actionName);
});
}
}, {
Expand All @@ -108,18 +128,19 @@ function withData(actions) {

return _class;
}(_react2.default.Component), _initialiseProps = function _initialiseProps() {
var _this3 = this;
var _this4 = this;

this.goToPage = function (actionName, page) {
_this3.setState(function (prevState) {
this.applyParams = function (actionName, params) {
_this4.setState(function (prevState) {
return (0, _immutabilityHelper2.default)(prevState, {
data: _defineProperty({}, actionName, { loading: { $set: true } })
data: _defineProperty({}, actionName, { loading: { $set: true } }),
appliedParams: _defineProperty({}, actionName, { $merge: params })
});
}, _this3.getData(actionName, { page: page }));
});
};

this.onNext = function (actionName, data) {
_this3.setState(function (prevState) {
_this4.setState(function (prevState) {
return (0, _immutabilityHelper2.default)(prevState, {
data: _defineProperty({}, actionName, { $merge: _extends({ loading: false }, data) })
});
Expand All @@ -129,7 +150,7 @@ function withData(actions) {
this.onError = function (actionName, error) {
var isAxiosError = Object.keys(error).includes("request"); // crude, but works
if (isAxiosError) {
_this3.setState(function (prevState) {
_this4.setState(function (prevState) {
return (0, _immutabilityHelper2.default)(prevState, {
data: _defineProperty({}, actionName, { $merge: { error: error, loading: false } })
});
Expand All @@ -145,6 +166,7 @@ function withData(actions) {
var noCache = options.noCache,
overrides = _objectWithoutProperties(options, ["noCache"]);

var appliedParams = _this4.state.appliedParams[actionName];
var action = void 0;
var childOptions = {};

Expand All @@ -156,26 +178,26 @@ function withData(actions) {
}

(0, _cache.observeData)(actionName, function () {
return action(_extends({}, _this3.props, overrides));
return action(_extends({}, appliedParams, _this4.props, overrides));
}, function (data) {
return _this3.onNext(actionName, data);
return _this4.onNext(actionName, data);
}, function (error) {
return _this3.onError(actionName, error);
return _this4.onError(actionName, error);
}, _extends({ noCache: noCache }, childOptions)).then(function (result) {
if (Array.isArray(result)) {
var _result = _slicedToArray(result, 2),
observeId = _result[0],
poll = _result[1];

_this3.setState(function (prevState) {
_this4.setState(function (prevState) {
return (0, _immutabilityHelper2.default)(prevState, {
observeIds: { $push: [observeId] },
polls: { $push: [poll] }
});
});
} else {
var _observeId = result;
_this3.setState(function (prevState) {
_this4.setState(function (prevState) {
return (0, _immutabilityHelper2.default)(prevState, {
observeIds: { $push: [_observeId] }
});
Expand Down
50 changes: 35 additions & 15 deletions src/withData.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,50 @@ function withData(actions) {
return class extends React.Component {
constructor(props) {
super(props);
// Set all queries to { loading: true, refetch: getData() }
// Set all queries to { loading: true, applyParams: fn(), refetch: fn() }
const data = {};
const appliedParams = {};
const actionNames = Object.keys(actions);
actionNames.forEach(actionName => {
data[actionName] = {
loading: true,
goToPage: page => this.goToPage(actionName, page),
refetch: params =>
this.getData(actionName, { ...params, noCache: true })
applyParams: params => this.applyParams(actionName, params),
refetch: () => this.getData(actionName, { noCache: true })
};
appliedParams[actionName] = {};
});
this.state = {
data,
polls: [],
observeIds: []
data, // Data object to send to child
appliedParams, // Filters, pages, sorts, etc (matches the structure of `data`)
polls: [], // Ids for polling - stop on unmount
observeIds: [] // Ids for store.js observables - unobserve on unmount
};
}

goToPage = (actionName, page) => {
this.setState(
prevState =>
update(prevState, {
data: { [actionName]: { loading: { $set: true } } }
}),
this.getData(actionName, { page })
componentDidUpdate(prevProps, prevState) {
// This code exists because the setState callback would not work for applyParams below
// May be related to https://github.com/facebook/react/issues/1740
const hasNewParams =
prevState.appliedParams !== this.state.appliedParams;
if (hasNewParams) {
const actionNames = Object.keys(this.state.appliedParams);
actionNames.forEach(actionName => {
if (
prevState.appliedParams[actionName] !==
this.state.appliedParams[actionName]
) {
this.getData(actionName);
}
});
}
}

applyParams = (actionName, params) => {
this.setState(prevState =>
update(prevState, {
data: { [actionName]: { loading: { $set: true } } },
appliedParams: { [actionName]: { $merge: params } }
})
);
};

Expand Down Expand Up @@ -64,6 +83,7 @@ function withData(actions) {

getData = (actionName, options = {}) => {
const { noCache, ...overrides } = options;
const appliedParams = this.state.appliedParams[actionName];
let action;
let childOptions = {};

Expand All @@ -76,7 +96,7 @@ function withData(actions) {

observeData(
actionName,
() => action({ ...this.props, ...overrides }),
() => action({ ...appliedParams, ...this.props, ...overrides }),
data => this.onNext(actionName, data),
error => this.onError(actionName, error),
{ noCache, ...childOptions }
Expand Down

0 comments on commit 9d72170

Please sign in to comment.