Things that can go wrong without a strictly typed language — Part II
a.k.a. “Los tipos son buena gente”
Part II: React
Overview
This post is about my experience of writing the same small example in Vue, Elm and React. Read the Part I if you haven’t done yet.
Compared to Vue, React is closer to Elm because both of them
- Generate html using functions (usually through JSX in case of React)
- Use one-way data binding
Vue instead:
- Is based on template (can also use JSX)
- Use two-way data binding
React is written in Javascript so all the issues related to type coercion that happened in Vue are also present in React. But React supports one-way data binding so the fix is simpler and more effective.
This is the React version:: https://codesandbox.io/s/7zj5jm562j
As you can see it suffers of the similar issues described in the first post: Javascript concatenate numbers because they are of type string, instead of adding them.
The simple fix is at line 13, from
return { ...product, quantity: value };
to
return { ...product, quantity: Number(value) };
this way we force the state of the app to be correct.
Compared to the Elm version, the React version is not checking if the value in the input field is a number or a string. This allow to hit Cancel on the last digit, turning it into zero.
To obtain a similar behavior in Elm example we would need to explicitly add a check on the length of the string and if the string is empty, associate it with a zero.
These are the three version side by side:
Consistency
In React
onChange = { e => this.changeQuantity(product.id, e.target.value) }
onClick = { e => this.changeQuantity(product.id, product.quantity + 1) }
In Elm
onInput <| ChangeQuantity product.id
onClick <| ChangeQuantity product.id (String.fromInt (product.quantity + 1))
These two lines are quite consistent in React and Elm (I admit it took me a while to it right in React, as it allows many variation, thank you Asako for showing me this version).
The difference here are:
- In React we need to pass the value explicitly (e.target.value), in Elm this is taken care automatically by onInput.
- In
onClick
, Elm force us to convert the number to a String type, because Elm is strictly typed and we want to reuse the sameChangeQuantity
message used foronInput
that has type signatureInt String
(where Int is the product.id and String is the new quantity) - Elm doesn’t have
this
- In the Elm version,
ChangeQuantity
is a function that return a message that is sent to theupdate
that change the state of the application (The Elm Architecture, TEA). In ReactchangeQuantity
is a function that change the state directly.
In Vue the input field is magically two-way bound:
<input type="number" v-model.number="product.quantity">
The button, compared to React and Elm, changes the quantity in place without calling any function. This is also part of the two-way binding. In Elm this is impossible, because the View is not allowed to change the state of the system but can only send out messages.
Boilerplate
In React Constructors, depending on the style of writing the code, require boilerplates on each component:
constructor() {
super(props);
this.handleClick1 = this.handleClick1.bind(this)
this.handleClick2 = this.handleClick2.bind(this)
}
More way to do one thing
In React it seems that “there are more way to do one thing” while Elm tend to follow the philosophy that “there is one way to do one thing”. This also derive from the flexibility of a language such as Javascript.
For example there are five way to bind a class method in React.
Moreover working with React require a firm grasp on bind/this/arrow_functions/class_field_syntax concepts while these concepts are absent in Elm.
In this sense Vue is also simpler as it rely less on these concepts.
Don’ts
React list of “better not to do this way”, from the official documentation:
“The render() function should be pure”, “[…] you should call super(props) […] otherwise […] can lead to bugs”, “You should not call setState() in the constructor()”, “Avoid introducing any side-effects or subscriptions in the constructor.”, “Avoid copying props into state, […] it creates bugs”, “You may call setState() immediately in componentDidMount(). It will trigger an extra rendering”, “You may call setState() immediately in componentDidUpdate() but note that it must be wrapped in a condition like in the example above, or you’ll cause an infinite loop”, “You should not call setState() in componentWillUnmount() because the component will never be re-rendered”
Typos
Similar to Vue, also React fail silently if we introduce certain typos, for example:
value={product.quanity}
JSON schema checker
The Elm version is also checking the integrity for the JSON file. Both Vue and React don’t have this type of checking so if the JSON schema change, it will break the application.
Thank you for reading!