Neophobe Plebeian Mumpsimus

    @ftw/redux-resources

    1.0.0-beta.17 • Public • Published

    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} />

    Keywords

    none

    Install

    npm i @ftw/redux-resources

    DownloadsWeekly Downloads

    18

    Version

    1.0.0-beta.17

    License

    MIT

    Unpacked Size

    357 kB

    Total Files

    180

    Last publish

    Collaborators

    • anthonymattox
    • danmartens
    • joshhepworth