1.2.0 • Published 6 years ago

react-high-order v1.2.0

Weekly downloads
1
License
MIT
Repository
github
Last release
6 years ago

react-high-order

A set of higher order and render props components that will help increasing your productivity on web development.

npm install --save react-high-order

Why react-high-order exists ?

In my opinion, React is the best tools for creating modular components that can be combined to something bigger and more complex (or maybe because I never use any other front-end library or framework). However, the more you code the more you will ask yourself, "why I have to rewrite the same component or the save behavior for every new project ?". I think it is time for me to create real world reusable react components that contain some logic inside such as a component that can call api and provide status to children, a component that can collect a function and will be called when user click submit. Let's get started

Example

When you want to create a component that contain simple api calling inside a component you would do this

    const api = () => new Promise((resolve) => {
      resolve('response from api');
    });
    
    class Example extends React.Component {
    
      static propTypes = {};
    
      state = {
        status: 'initialized',
        error: null,
        response: null
      };
    
      callApi = () => {
        this.setState({ status: 'requesting' });
        api()
          .then((response) => this.setState({
            response,
            status: 'success'
          }))
          .catch((error) => this.setState({
            error,
            status: 'failure'
          }));
      };
    
      render() {
        const { status, response, error } = this.state;
        return (
          <div>
            <p>{status}</p>
            {response && (
              <p>
                {/* or render something else */}
                {response}
              </p>
            )}
            {error && (
              <p>{error}</p>
            )}
            <button onClick={this.callApi}>
              {status === 'requesting' ? 'Requesting...' : 'Call API'}
            </button>
          </div>
        );
      }
    }

But with react-high-order, you only have to do this

    import { Caller } from 'react-high-order'
    
    const Example = () => (
      <Caller api={api}>
        {(wrappedApi, { status, response, error, reset }) => {
          return (
            <div>
              <p>
                {status.isInitial && 'Initialized'}
                {status.isRequest && 'Requesting'}
                {status.isSuccess && 'Success'}
                {status.isFailure && 'Failed!'}
              </p>
              <p>{response}</p>
              {error && (
                <button onClick={reset}>reset</button>
              )}
              <button onClick={wrappedApi}>
                {status === 'requesting' ? 'Requesting...' : 'Call API'}
              </button>
            </div>
          );
        }}
      </Caller>
    )

What Caller do is just wrapping the api you provide with status and then transfer to its children. This technique is officially called as render props. The code is more cleaner and declarative, you don't have to be overwhelmed with lots of state.

API

Mostly react-high-order components will be hoc or using render props technique. The purpose of this repo is to help you increase your productivity in web development without messing core logic in your app.

TOC

Render Props

Caller

description : accept child only as a function and provide wrappedApi, status, ...others to it.

use case : Call api within component

parameters

propstypedefault
children (*)fn(options) => react element-
api (*)fn => promise-
onRequestfn => void() => {}
onSuccessfn => void() => {}
onFailurefn => void() => {}

children options

parameterstypeinitial statedescription
wrappedApifn-the same fn as api from props but wrapped with status
statusobjectnull{ state:<String: 'isInitial', 'isSuccess', 'isFailure'>, isInitial:<Bool>, isRequest:<Bool>, isSuccess:<Bool>, isFailure:<Bool> }
response-nullresolve from api
error-nullreject from api
resetfnfnreset status, response, error to initial state

example

  <Caller api={api}>
    {(wrappedApi, { status, response, error, reset }) => {
      return (
        // You can do whatever you want
        // show status
        // show response
        // show error from api
        // even calling reset  if you want.
      );
    }}
  </Caller>

Notes You can change state of the status by doing this

import { Caller } from 'react-high-order'

Caller.REQUEST = 'isPending'
Caller.SUCCESS = 'isFulfilled'
Caller.FAILURE = 'isFailure'

export default Caller

// then use Caller from above
// the result will be like

import Caller from '../file above';

<Caller>
  {(wrappedApi, { status }) => (
    // status = { isInitial<Bool>, isPending<Bool>, isFulfilled<Bool>, isFailure<Bool> }
  )}
</Caller>

Activator

description accept child only as a function and provide 'createAction' that you can input callback to call.

use case: Show modal before deleting something (great for using with Caller)

parameters

propstypedefault
children (*)fn(options) => react element-
action (*)fn => (promise or void)-
actionIsPromiseboolfalse
resetAfterActionbool or objectfalse

children options

parameterstypeinitial statedescription
activatefn-set activated to true and can accept params and store it for later use
activeboolfalsea boolean that tell activate sth (such as modal)
createActionfn-the same fn as action from props but wrapped to toggle activated
resetfn-reset to initial state
paramsarraynullarray of params that you provide when use activate(...params)

example

<Activator actionIsPromise resetAfterAction={{ isRequest: true }}>
    {({ activate, active, params, createAction, reset }) => (
      // params[0] = 'item to delete'
      <div>
        <Modal open={active}>
          <button onClick={createAction(deleteApi)}>call action</button> // deleteApi will receive 'item to delete' as first parameter
          <button onClick={reset}>cancel</button>
        </Modal>  
        <div>
          <button onClick={() => activate('item to delete')}>activate</button>
        </div>
      </div>
    )}
</Activator>

You can change static status in Activator as same as Caller

Collection

description control collection contain adding new item, update, duplicate, remove item

use case: show list in form that user can add more and edit each item

parameters

propstypedefault
children (*)fn(options) => react element-
initialItemsarray of string, object-

children options

parameterstypeinitial statedescription
itemsarray of string, object[]the same format as initialItems
addToIndexfn(newItem, index)-accept 2 params newItem: item that will be added and index: index to be added in front
duplicateIndexfn(index, callback)-accept 2 params index: item index that will be duplicate and callback: (item of that index) => new duplicated Item
onItemChangefn(newItem<fn, string, object>, predicate)-accept 2 params newItem: if it is function accept item that pass predicate and return a new one, else new item predicate a predicate to find item to be changed.
onItemChangeByIndexfn(newItem<fn, string, object>, index)-same as onItemChange but change predicate to index to specify which index to be changed.
removeItemfn(predicate)-accept 2 params item and index return true will remove the item
removeIndexfn(index)-remove the item that is the same as index
renderItemsarray-a wrapped array that can be used for cleaner code, contain all wrapped function item, onDuplicate, onChange, onRemove (check out example for more detail usage)

other methods

nametypedescription
appendDuplicateNamestatic fn(name) => name (copy)a util fn for append 'copy' to name (use with duplicate method)
resetItemsfn(items, callback)to reset items (using setState internally, so you can inject callback as normal)

example

  <div>
    <h2>Fully Control</h2>
    <Collection
      ref={this._collection1} // you can use ref to access `resetItems`
      initialItems={this.state.items}
    >
      {({ items, addToIndex, onItemChange, duplicateIndex, removeIndex }) => (
        <div>
          <ul>
            {items.map((item, index) => (
              <li key={index}>
                {item.name}
                <button
                  onClick={() => onItemChange({ name: `${item.name}+` }, (_, i) => index === i)}
                >append +
                </button>
                <button onClick={() => duplicateIndex(index, () => ({ name: Collection.appendDuplicateName(item.name) }))}>dup</button>
                <button onClick={() => removeIndex(index)}>remove</button>
              </li>
            ))}
          </ul>
          <button onClick={() => addToIndex({ name: 'test' })}>
            add
          </button>
        </div>
      )}
    </Collection>
    <button onClick={this.reassignItems1}>reset</button>
    <hr />
    <h2>Light Version</h2>
    <Collection
      ref={this._collection2}
      initialItems={this.state.items}
    >
      {({ renderItems, addToIndex }) => (
        <div>
          <ul>
            {renderItems.map((source, index) => {
              const { item, onChange, onRemove, onDuplicate } = source;
              return (
                <li key={index}>
                  {item.name}
                  <button
                    onClick={() => onChange({ name: `${item.name}+` })}
                  >append +
                  </button>
                  <button onClick={() => onDuplicate({ name: Collection.appendDuplicateName(item.name) })}>
                    dup
                  </button>
                  <button onClick={onRemove}>remove</button>
                </li>
              );
            })}
          </ul>
          <button onClick={() => addToIndex({ name: 'test' })}>
            add
          </button>
        </div>
      )}
    </Collection>
    <button onClick={this.reassignItems2}>reset</button>
  </div>
1.2.0

6 years ago

1.1.1

6 years ago

1.1.0

6 years ago

1.0.4

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago