acdlite / recompose

A React utility belt for function components and higher-order components.
MIT License
14.76k stars 1.26k forks source link

Function order of compose #235

Closed rhythnic closed 8 years ago

rhythnic commented 8 years ago

Just started to incorporate recompose into my work flow, and I'm loving it so far. One thing I noticed is that the "compose" util is compared to traditional compose functions and lodash's flowRight, in which the functions are applied from right to left. It seems like recompose's "compose" util applies the functions from left to right. If that is the case, it might be worth updating the docs to say that.

istarkov commented 8 years ago

It applyes functions in the same order as lodash, you can even use lodash compose to compose recompose HOCs. And here is the recompose magic, as every function returns component, so having React tree their looks like applied in the flow order. Just take a piece of paper and write this on it.

rhythnic commented 8 years ago

Ok, I can see the difference. It builds a HOC by running the functions from right to left, but during execution, the props are passed from left to right.

istarkov commented 8 years ago

As a simple example

const a = x => x * 2
const b = x => x + 2

const param = 1;
a(b(param)); // it's compose result is 6
const a = fn => x => fn(x * 2);
const b = fn => x => fn(x + 2);

const param = 1;
a(b(x=>x))(param); // it is the same compose function as above but result is  === 4
nmrshll commented 6 years ago

Just to add a real life example as documentation :

const contentPageData = compose(
  mapProps(({ match }) => ({
    contentPageID: ((match || {}).params || {}).contentPageID,
  })),
  graphql(
    gql`
      query ContentPage($id: ID!) {
        ContentPage(id: $id) {
          id
          title
          slug
          content
        }
      }
    `,
    {
      options: ({ contentPageID }) => ({ variables: { id: contentPageID } }),
    }
  ),
  spinnerWhileLoading,
  errorIfError,
  connect({
    actions: [contentPageStore, ['setCurrentPageContent']],
  })
)

See how mapProps() creates the prop contentPageID, then graphql() uses that prop to fetch with the right params. Then the functions spinnerWhileLoading and errorIfError use props that the graphql() hoc injected.

Basically, when composing HOCs, think the same order as usual code: any value one HOC uses has to be created / passed by previous HOCs.

Ravikeshpr commented 5 years ago

What if two of the HOC want the instance of main Component to operate?

nmrshll commented 5 years ago

what do you mean the main Component ? The component wrapped by the HOCs ? Well the whole composed HOC operates on the wrapped component, but each HOC operates on the component modified by the HOCs below it.

HOCs can:

So basically any HOC can operate on the main component as long as the HOCs that operated before it (the ones below/right in the argument order) didn't modify it too much in a way that would make the next HOC error (that's on you as the programmer to watch out for).