(If you have a bit of time and have not yet watched the introduction talk by Sophie Alpert, Dan Abramov and Ryan Florence, you definitely should.) At first, the community reacted with "a little" scepsis, however, until today, Hooks and Function Components have become the defacto standard way of developing React components for many teams.
This blog post will not directly cover the benefits behind and reasons for Hooks. For that, you can check out the official release blog post that collects some resources and goes into more detail. Nevertheless, I want to emphasize on the killer feature of React Hooks: The reusability of "React-ful" logic.
Companies using Vue.js
By "React-ful" I mean logic, that uses React's internals like component state and the component lifecycle.
Hooks were the first API, that allowed the encapsulation of that kind of logic in a module, without introducing name clashes or opaque APIs where developers would wonder, where a variable came from (which was the case with earlier approaches like mixins or Higher Order Components).
- This killer feature even intrigued Evan You, the creator of Vue. On Twitter he noted that Hooks are "objectively better" as a composition mechanism than earlier approaches like mixins, higher order components or render props (source).
- However, he had a couple of gripes with newly introduced API and set out to find something better, that fits right into the world of Vue. The result of that work is Vue 3's new reactivity system coupled with the new composition API.
- In case you are not convinced of this new API, I recommend the official RFC page that goes into great detail about the motivation behind the composition API. In this post, I'd like to compare these two approaches to find differences, commonalities, and allow you to learn something about your own setup, no matter if you are using Vue or React.
React vs Vue: Abstract of the comparability
Disclamer: I am mainly a React developer, however I admire the simplicity of the composition API. I will try to be as unbiased as possible, but in the end, I still have way more experience with React hooks, which definitely affected my mental model, so that some Vue concepts might be uncommon for me.
I will very happily let you correct me if I get anything wrong about the Vue side of things 😊. In order to compare the two approaches, I built a simple app with both libraries. A Pokémon team composer. It is built from 2 simple screens, with 3 relevant components. On the first screen, you can see your current team, remove Pokémon and use the search bar at the bottom to add new ones to your roster. When you click on the name of one of your team members, you navigate to a details screen, where you can edit the nickname and a comment for the selected Pokémon.
Availability of Vue and React builders
The data for this app is persisted in a simple backend, that provides endpoints for fetching your complete team, searching for Pokémon by name, and handling the details for each one.
- At first, we want to take a look at handling of simple UI state with hooks and the composition API.
- For this case, we define state as a piece of data that changes over time and is being reflected by the UI.
As an example, we will take a look at the Search component first.
- In here, we want a textbox and send off fetch requests whenever the content of this input changes.
- For starters, we will omit the data fetching and focus on the state/UI synchronisation.
- Here you can see the most basic use case of hooks: Whenever we need data that changes over the lifecycle of a component, we define component state by using the useState hook.
React vs. Vue in Testing
- We pass in the initial value of this state slot and it returns a tuple, an array with two values.
- The first is the current value of the state variable, and the second is the updater function that has to be used to change this value.
- So whenever our state changes (because someone called setSearchValue) React calls this function again, dropping all variables from the previous execution while only keeping the contents of the component state across calls.
- This way, the variables in that function block never change and can be defined as const because they are redeclared with every rerender.
React vs Vue: Head-to-head comparison
Additionally this means, that state itself is never mutated in React. It's just replaced by calling the state setter before React decides on the best time to rerender the component instance with the new state value. This way, our own code remains completely stateless, wich is a very important nuance that is needed for Concurrent Mode to work optimally.
At first glance, this looks pretty similar to the React version: Whenever we need data that changes along the lifetime of a component instance, we can use the ref function from the new composition API.
This function returns an object with one property: value. And in this field, Vue keeps the current value of the state.
The main benefit of this approach is, that Vue can track assignments of this property (by using Proxies or setters).
This means, that you as a developer don't need to call a specific setter function. You can just assign new values to this value property and even mutate objects or arrays inside of the ref.
- Additionally we add an event listener to the input event, and assign the new value to the ref.
- This triggers Vue's reactivity system and results in an updated UI aswell. Next, we want to take a look at derived state: Values that can be computed by using the current values of other component state.
- Examples for that are the validation of forms (which only depend on the current values of the form).
- To make it a bit simpler, we will just display the length of the entered text, that we can later use to decide if we want to send a search request (because maybe we don't want to send any requests with 2 or less characters).
- Since React is calling our function whenever state changes, we can use the value inside of the searchValue const and calculate the length directly in the render function without any React specific logic.This way, the computed value will always be in sync with the current state and we don't need to manually synchronize states.
- In Vue on the other hand, we have to approach this task a bit differently. Since the setup function is only called once per component instance, we cannot simply derive some state from a variable since all state variables are encapsulated inside of mutable ref objects.
- Our goal is, to listen for changes inside of the search ref and do some computations whenever the ref changes.
- This is exactly what the computed helper is supposed to do: We pass a getter function, that calculates the current value of this new ref.
- In there, we can access other reactive objects (props for example) and Vue will handle the recomputation of this getter function whenever a used dependency changes.
- computed then returns a read only ref. Do note however, that we are accessing the value property of the search ref.
As stated above, Vue needs this wrapper around the value so that it can track dependencies like the computed length value in line 7. Here we can nicely see the difference between the mutable world of Vue and the immutable way of thinking in React: With mutability, you have to make sure that no code is mutating the source data without your code noticing it.