Note II: This article have some more advanced concepts but I’ll try to do as simple as possible for all audiences.
If we think in duck typing by the definition, is to pass a “mallard” when whoever receives is expecting a “duck”, but this still works, because the “receiver” expecting a “duck” don’t care about the race (type/instance) but some of it behaviours (methods).
But how to apply it in the React’s context? This is tricky, but is something we’re going to achieve in this post!
React is a view framework/library (yes, is a long discussion) but the important thing here is, its focus is applied on build view components. Let’s stick to the original React and apply our examples on the web, but the concept is valid for native and other vertents of React views.
This principles is related to the Hollywood mindset of the production houses calling the actors for roles, instead of the inverse:
“Don’t call us, we’ll call you”
In our (tech) world, this principle talks about inversion of control in runtime. Let me explain this in way you’re going to understand:
Yes, React is based on that. Who calls the “render” and other lifecycle methods? For sure isn’t you!
This is why React is also treated as a framework (polemic discussion alert!), because of this inversion of control in runtime it takes the responsibility of call what needs to be called, on the right time. You just care about implement the right methods and voilà!
Where Duck Typing fits there?
The Hollywood principle and Duck Typing have a strong relation. If we combine this inversion of control, making this “controller/wrapper” call the right methods, not importing the instance but the behaviour, we’re going to accomplish what we want.
Following this principle, let’s build a example of something pretty generic, where we apply this concepts in a simple way:
Let’s build a simple player control with three buttons: stop, pause and play. The trick here is to make it generic using both principles we’ve discussed above. It should take control of what is set inside of it (children) and don’t matter about the instance type, but it should have the three expected behaviours, load, pause and play (common for HTML media elements):
Nothing new but, if we create a Component that acts just like a media element?
It have all the three methods that PlayerControl is expecting. And then we can simply show the three players in action:
And it works! So, this is Duck Typing in action. As long it behaves as the PlayerControl expects, it should work!
It’s OK to use refs for media elements, but the problem is to use for everything. There’s another problem, the PlayerControl not explicitly declare it’s expecting these three methods (load, pause and play) by the elements, causing a lot of confusion while using it. If you have tests and docs, probably you have less problem with that, but as the React docs says:
“Avoid using refs for anything that can be done declaratively.
For example, instead of exposing
close()methods on a
Dialogcomponent, pass an
isOpenprop to it.”
As the PlayerControl expects only media elements, it simply don’t care about the type of instance (if is a React Component or a HTMLMediaElement). But let’s make the PlayerControl assume it’s receiving a Component and if isn’t we can just wrap in a component anything else is going inside. The new PlayerControl should be like this:
As you can see, it will try to inject three properties on the child component. If the child isn’t expecting it React will do the job to warn you. It’s now much more in the “React way”, but now we’ve lost the inversion of control and the behaviour calls.
Basically we just transferred both the inversion of control and the Duck Typing to the HOC. You can inject any media element inside (or Component with these methods, but we’re avoiding this) and it’s going to work properly.
Apply the Duck Typing concept to React is kind tricky but is achievable. Of course, we can’t use 100% of the pattern but we can adapt in the React’s terms to work properly (avoiding the use of refs).
This example is pretty simple but you can apply to many other components. For instance a generic “Wizard” component which render formulary steps and expecting then to be valid to enable the “next” button. Other example can be widgets in a dashboard, where a plugins can have different view states (as configuration and visualization), managed by the Widget component who wraps it.
The Modus Operandi is always the same, expect the child Component to be listening to some properties and that they’re going to react to it.