@ftw/redux-resources
v1.0.0-beta.17
Published
State management utilities for Redux.
Downloads
25
Keywords
Readme
Catalyst Redux
State management utilities for Redux.
⚠️ Definitely a WIP ⚠️
Getting Started
Creating a Configuration
import {
Configuration,
ResourceClass,
attributes,
relationships
} from '@ftw/redux-resources';
const Post = ResourceClass({
type: 'posts',
attributes: {
title: attributes.string,
content: attributes.string,
publishedAt: attributes.date
},
relationships: {
comments: relationships.hasMany('comments', {
inverse: 'post'
})
}
});
const Comment = ResourceClass({
type: 'comments',
attributes: {
content: attributes.string
},
relationships: {
post: relationships.hasOne('post', {
inverse: 'comments'
})
}
});
const ResourcesConfiguration = new Configuration([Post, Comment]);
Providing the Configuration
The <Provider />
component is used to give any component in your application
access to your Configuration
instance via React's context feature. It must
either be a descendent of react-redux's <Provider />
or have the Redux store
passed in via its store prop.
import { Provider } from 'react-redux';
import { Configuration, Provider as ResourcesProvider } from 'catalyst-redux';
const ResourcesConfiguration = new Configuration([Post, Comment]);
function Application() {
return (
<Provider store={store}>
<ResourcesProvider configuration={ResourcesConfiguration}>
{/* Your app goes here. */}
</ResourcesProvider>
</Provider>
);
}
Configuration Options
Request Options
The requests made and responses received by a resource module can be transformed
by passing an options object to Configuration
:
import { Configuration } from 'catalyst-redux';
const Resources = new Configuration(
[
// Resource classes
],
{
transformRequest({ data, headers }) {
headers = { ...headers, Authorization: 'Bearer 1234567890' };
return { data, headers };
},
transformResponse(data) {
return { ...data };
}
}
);
Actions
findAllAction(resourceClass: Class, ?options: Object)
Example:
Resources.findAllAction(Post, {
filter: {
authorId: 7
},
include: ['comments']
});
createAction(resourceClass: Class, attributes: Object, ?options: Object)
Example:
Resources.createAction(
Comment,
{ content: 'First!' },
{
relationships: {
post: { type: 'posts', id: '1' }
}
}
);
updateAction(resourceClass: Class, resourceID: number | string, attributes: Object, ?options: Object)
Example:
Resources.updateAction(
Comment,
6,
{ content: 'Something meaningful.' },
{
relationships: {
post: { type: 'posts', id: '1' }
}
}
);
The connectResources
HOC
This higher-order component allows you to easily fetch multiple resources and pass them down to the wrapped component as props.
import { connectResources } from 'catalyst-redux';
class TodoList extends React.PureComponent {
componentDidMount() {
this.findResources();
}
render() {
<ul>{this.props.items.map(() => <li>{item.content}</li>)}</ul>;
}
}
connectResources(props => {
return {
currentUser: { type: User, id: props.currentUserID },
items: { type: TodoItem }
};
})(TodoList);
const ConnectedPostsIndex = connectResources(props => {
return {
posts: {
type: Post,
options: {
include: 'comments',
filterResults: post => post.tags.contains(props.tag)
}
}
};
})(PostsIndex);
<ConnectedPostsIndex tag="featured" />;
The connectResource
HOC
This
higher-order component
allows you to easily build components which can both create new resources and
update/destroy existing resources. The wrapped component (CommentForm
in the
following example), is passed these props by connectResource
:
resource: Object | null
resourceStatus: string | null
onCreate: (attributes: Object, options?: Object) => void
Dispatches an
action to create a resource using the passed attributes.
onUpdate: (attributes: Object, options?: Object) => void
Dispatches an
action to update the resource using the passed attributes.
onDestroy: () => void
Dispatches an action to destroy the resource.
import { Resource, connectResource } from 'catalyst-redux';
import type { ResourceProvidedProps } from 'catalyst-redux';
class Comment extends Resource {
static type = 'comments';
}
type Props = ResourceProvidedProps;
class CommentForm extends React.PureComponent<Props> {
state = { content: '' };
handleChange = event => {
this.setState({ content: event.currentTarget.value });
};
handleSubmit = event => {
event.preventDefault();
this.props.onCreate({
content: this.state.content
});
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<textarea value={this.state.content} onChange={this.handleChange} />
<button>Add Comment</button>
</form>
);
}
}
const ConnectedCommentForm = connectResource(Comment)(CommentForm);
The connectResourceForm
HOC
This HOC makes it easy to store temporary state for a resource's attributes
before the resource is created/updated. The wrapped component is passed these
props in addition to the ones provided by connectResource
:
onSave: (eventOrOptions?: SyntheticEvent<*> | Object = {}, options?: Object = {}) => void
Calls either onSave
or onCreate
depending on the state of
the resource. Optionally accepts an event as the first argument and calls
preventDefault
(which allows it to be used directly in a form's onSubmit
).
onReset: () => void
Clears the stored attribute values, effectively
resetting the form.
inputProps: (key: string) => { value: string, onChange: (event: SyntheticInputEvent<*>) => void }
Given the name of an attribute, it
returns an object with value
and onChange
props which can be spread onto an
<input />
, <select />
, etc.
import { Resource, connectResourceForm } from 'catalyst-redux';
import type { ResourceFormProvidedProps } from 'catalyst-redux';
class Post extends Resource {
static type = 'posts';
}
type Props = ResourceFormProvidedProps;
const PostForm = ({ onSave, onReset, inputProps }: Props) => {
return (
<form onSubmit={onSave}>
<label for="title">Title</label>
<input id="title" type="text" {...inputProps('title')} />
<label for="content">Content</label>
<textarea id="content" {...inputProps('content')} />
<button type="submit">Save</button>
<button type="button" onClick={onReset}>
Reset
</button>
</form>
);
};
const ConnectedPostForm = connectResourceForm(Post)(PostForm);
The ConnectedPostForm
component can now be used to either create a new post:
<ConnectedPostForm />
or to edit an existing one:
<ConnectedPostForm resourceID={4} />