npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-hoc-pipe

v1.0.0-beta.2

Published

React HOC Pipe

Downloads

5

Readme

React HOC Pipe

Build Status Coverage Status npm version jest

Chain, setup and reuse Higher-Order Components easily accross your React application.

Motivation

On a React project, you often use the same HOC, with sometimes the same arguments. pipe() enable to create a pipe of HOC, and reuse it accross your application.

A predefined HOC pipe, named pipeRequest, is also provided. You can create your own pipe, or use pipeRequest and extends it with your HOCs.

Do you want to see a concrete example now? See HOC pipe request or a full reusable HOC pipe


Install

npm install react-hoc-pipe --save

or with yarn

yarn add react-hoc-pipe

HOC Pipe

pipe() is a reusable pipe of HOC.

How to create a pipe ?

const hocs = {
  myFirstHOC: { ... }
  mySecondHOC: { ... }
}

const myPipe = () => pipe(hocs)

Then, reuse it !

class Component extends React.Component {
  ...
}

const App = myPipe()
  .myFirstHOC(params)
  .mySecondHOC()
  .render(Component)

...

render() {
  return <App />
}

How to define HOC ?

const hocs = {
  withData: {
    externalsParams: [],
    HOC: externalsParams => data => App => {
      return class Request extends React.Component {
        render() {
          return <App {...this.props} {...data} />
        }
      }
    },
  },
}
  • externalsParams - optional - functions to set parameters that will be used inside HOC. More detail

  • data - optional - yours HOC arguments

  • App - React Component

Full example:

const hocs = {
  withData: {
    externalsParams: [],
    HOC: (externalsParams) => (data) => App => {
      return class Request extends React.Component {
        render() {
          return <App {...this.props} {...data} />
        }
      }
    },
  }
}

const myPipe = () => pipe(hocs)

...

const App = myPipe()
  .withData({ foo: 'bar' })
  .render((props) => {
    return <div>{props.foo}</dib>
  })

I know, this example is completely useless. But it's simple. You can then build your complex HOC.

What are externalsParams ?

externalsParams are functions to set parameters that will be used inside HOC. It's usefull because you can set paramters before or after the HOC call in the pipe.

const hocs = {
  withData: {
    externalsParams: ['addData']
    HOC: (externalsParams) => (data) => App => {
      return class Request extends React.Component {
        render() {
          // addData[0] correspond to the first argument of addData()
          const externalsData = externalsParams.addData[0];

          return <App {...this.props} {...data} {...externalsData} />
        }
      }
    },
  }
}

const myPipe = () => pipe(hocs)

...

const App = myPipe()
  .withData({ foo: 'bar' })
  .addData({ foo2: 'bar2' })
  .render((props) => {
    return <div>{props.foo} {props.foo2}</dib>
  })

"Wait, why do not simply use only withData() and pass all data through it" :question:

Good question ! And the anwser is simple: sometimes, you want to reuse a pipe, with the same parameter 95% of the time, and 5% remaining, you want to override it.

Example:

const hocs = {
  request: {
    externalsParams: ['renderLoader'],
    HOC: ...,
  }
}

const Loader = () => <div>...</div>

const myPipe = () => pipe(hocs).renderLoader(Loader)

...

const Page1 = myPipe()
  .request(...)
  .render(...)

...

const Page2 = myPipe()
  .request(...)
  .render(...)

You defined your spinner only once, and it will be use into all of your myPipe(), until you override it. If you want to override it for a specific component, it's simple:

const Page1 = myPipe()
  .request(..)
  .renderLoader(() => <div>Loading...</div>)
  .render(...)

:warning: externals params doesn't care about the call order. Externals parameters can be call before or after his HOC. Both Page1 and Page2 are equivalent:

const Page1 = myPipe()
  .request()
  .renderLoader()
  .render()

const Page2 = myPipe()
  .renderLoader()
  .request()
  .render()

However, the call order of HOC is important !

Page1 and Page2 are not the same:

const Page1 = myPipe()
  .connect(...)
  .request(...)
  .render(Component)

const Page2 = myPipe()
  .request()
  .connect()
  .render(Component)

The classique HOC syntax correspond to this:

const Page1 = connect(...)(request(...)(Component))

const Page2 = request(...)(connect(...)(Component))

Real world examples

Do you want a full real usefull example ? Well. I made a HOC pipe focus on the request handling.

Pipe Request

pipeRequest() is a predefined HOC pipe, focus on the request feature. It makes it possible to perform a request, show a loader, map request results to props, and then render your component,

import React from 'react'
import { pipeRequest } from 'react-hoc-pipe'

class MyComponent extends React.Component {
  ...
}

const App = pipeRequest()
  .request((props) => fetch('http://website.com/posts'))
  .mapRequestToProps((response) => ({ posts: response.posts }))
  .renderLoader(() => <div>Loading...</div>)
  .render(MyComponent)

Just below, the documentation of pipeRequest()

import { pipeRequest } from 'react-hoc-pipe'

/**
 * hocs are optional
 */
const hocs = {
  ...
}

const App = pipeRequest(hocs)
  /**
   * async request. Must return a Promise
   *
   * optional
   */
  .request(props => fetch('...'))

  /**
   * map the request results to props
   *
   * By default, request results are not sent as props to the render()
   * Because sometime, you doesn't want to get the results directly.
   * It's the case if you use redux actions with dispatch. You perform a request,
   * but don't want to get the result from the Promise
   *
   * optional
   */
  .mapRequestToProps(response => ({
    foo: response.bar,
  }))

  /**
   * Functionnal component or class component
   *
   * It's render during the request process
   * If there is no renderLoader, final render is use
   *
   * optional
   */
  .renderLoader(() => <div>is Loading...</div>)

  /**
   * Functionnal component or class component
   * Final render
   *
   * required
   */
  .render(Component)

HOC with arguments, like redux connect()

Here, I will use pipeRequest(), but if you doesn't need request handling, you can use pipe()

import { pipeRequest } from 'react-hoc-pipe'
import { connect } from 'react-redux'

const hocs = {
  connect: {
    HOC: (externalsParams) => (mapStateToProps, mapDispatchToProps) => App => {
      return connect(mapStateToProps, mapDispatchToProps)(App);
    },
    // Or the simpler and shorter version
    HOC: (externalsParams) => connect
  }
}

const App = pipeRequest({ connect })
  .connect(
    mapStateToProps,
    mapDispatchToProps,
  )
  .request(...)
  .mapRequestProps(...)
  .renderLoader(...)
  .render(props => <div>...</div>)

HOC without arguments, like withRouter()

If you use externals HOC without argument, like withRouter(), the syntaxe is a bit different than HOC with arguments, like connect() .

connect: connect(params)(App)

withRouter: withRouter(App)

As you can see, connect() take params and return another function, while withRouter() directly take the React component as parameter. So the externals HOC config is a bit different.

Note: in the following examples, externalsParams are useless.

import { pipeRequest } from 'react-hoc-pipe'
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'

const hocs = {
  connect: {
    HOC: (externalsParams) => connect
  }
  withRouter: {
    HOC: (externalsParams) => () => withRouter
  }
}

const App = pipeRequest(hocs)
  .connect(
    mapStateToProps,
    mapDispatchToProps,
  )
  .request(props => fetch('...'))
  .withRouter()
  .render(props => <div>...</div>)

Enhance redux connect() with externals params

externals params can be usefull for defined mapDispatchToProps, if you often use the same actions.

import { pipeRequest } from 'react-hoc-pipe'
import { connect } from 'react-redux'
import { fetchUser } from 'src/store/actions'

const hocs = {
  connect: {
    externalsParams: ['mapDispatchToProps']
    HOC: (externalsParams) => (mapStateToProps, mapDispatchToProps) => {
      const finalMapDispatchToProps = externalsParams.mapDispatchToProps || mapDispatchToProps
      return connect(mapStateToProps, finalMapDispatchToProps)
    }
  }
}

const myPipeRequest = () => pipeRequest(hocs).mapDispatchToProps({ fetchUser })

...

const App = myPipeRequest()
  .connect(mapStateToProps)
  .request(props => props.fetchUser()) // fetchUser is binded to redux store
  .render(props => <div>...</div>)

Full reusable HOC pipe

import { pipeRequest } from 'react-hoc-pipe'
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import { fetchUser } from 'src/store/actions'

const hocs = {
  connect: {
    HOC: () => connect,
  },
  withRouter: {
    HOC: () => () => withRouter,
  },
}

const pipeWithUser = externalsHOCs =>
  pipeRequest({ hocs, ...externalsHOCs })
    .withRouter()
    .connect(
      (state, props) => {
        const userId = props.match.params.userId
        return {
          userId,
          user: state.users[userId],
        }
      },
      { fetchUser },
    )
    .request(props => props.fetchUser(userId))
    .renderLoader(() => <div>Fetching user...</div>)

Then, reuse it !

const UserView = (props) => (
  <div>
    <div>{props.user.firstName}</div>
    <div>{props.user.lastName}</div>
  </div>
)

const User = pipeWithUser().render(UserView)

...

class Page extends React.Component {
  render() {
    return (
      <div>
        <User />
      </div>
    )
  }
}