Echo JS 0.11.0

<~>
planttheidea 2204 days ago. link parent 1 point
Valid question! 

The benefit of using a partial application function lies primarily in testability. Under normal circumstances to test the componentDidMount method you would need to use something like enzyme to physically mount the component, wait for the rendering to occur, and then introspect the component instance to determine if the actions you needed to happen actually happened. This is both slow and tedious. By making a higher-order function that receives the instance, you no longer need to do this because you can very easily mock the instance you need and execute componentDidMount as you would a generic utility. It converts lifecycle methods, instance methods, even the render method into pure functions, and that encapsulation allows for much faster and simpler testing.

There are additional benefits, as you can now leverage shorthand arrow syntax for basic transactional methods like updating state (all the examples in the README showcase this), and several of our team members comment about how the clear separation of concerns between business logic and rendering feels more natural (which is why so many people like functional components), but these are bonuses compared to the real benefit of simpler testability.

Replies

kirilloid 2201 days ago. link 2 points
I don’t understand what’s the problem with creating an instance of a component manually (just with `new`) and calling methods in a regular way. If calling `componentDidMount` in a “detached” way (BTW you can call it with `.call`) is OK.
planttheidea 2199 days ago. link 1 point
The goal here is functional purity, so that all of your instance methods are encapsulated and easily testable. There are usually a decent amount of hoops to jump through if you want to test lifecycle methods, especially ones revolving around updates.

There is also the benefit of code terseness. Example:

function componentDidMount() {
  const {getThing, id} = this.props;

  getThing(id);
}

vs

const componentDidMount = ({props: {getThing, id}}) => getThing(id);

The latter is much less verbose, while at the same time being self-documenting. In the former case, you need to introspect the function to determine what is being used (a function and id from props), whereas that information is readily available in the function signature in the latter case.

There are many other examples where your mitigating argument can be applied ... for example, recompose lets you add lifecycle methods to functional components, when converting it to a class is "not that hard". This paradigm has helped greatly speed up my velocity, but like anything else, opinion and preference come into play, so YMMV.
Hartbeatnt 2201 days ago. link 1 point
makes sense! thanks for the explanation 👍

So I'm guessing this is more targeted towards apps that are not already using a utility for currying, such as ramda or lodash. Or does it provide additionally utility specific to lifecycle methods that other libraries might not provide?
planttheidea 2199 days ago. link 1 point
while i love functional utility libraries like ramda, the goals with this library are a little different because they are react-specific.

first, making your lifecycle and instance methods pure functions which receive the instance means that you can test them without actually creating an instance ... you can pass a mock object with the appropriate values that you know exist because of the contract. this makes testing both simpler and much faster. it also generally means writing simpler code that reads more declaratively.

second, when coupled with the createComponent method, you can build a stateful, lifecycle-aware, ref-capable component from a standard functional component. this allows for easier augmentation of an existing "dumb" component without having to refactor the entire file to use the class infrastructure.

third, if used across the board, it creates a more consistent implementation of React ... smart and dumb components have the same interface, just additional options applied.

the inspiration behind this is a React-like implementation called deku => https://github.com/anthonyshort/deku. it is kind of a dead project now, but i always appreciated the simplicity of the code you would write with it, so i found a way to make a similar interface with React.