I created the exact same app in React and Vue. Here are the differences.
So you can see that we have passed the same data into both, but theyâre simply labelled differently. So passing initial data into our components is very, very similar. But as weâve mentioned, how we go about changing this data differs between both frameworks.
Letâs say that we have a data element called name: âSunilâ
In Vue, we reference this by calling this.name. We can also go about updating this by calling this.name = âJohnâ. This would change my name to John. Iâm not sure how I feel about being called John, but hey ho, things happen! đ
In React, we would reference the same piece of data by calling this.state.name
While this essentially does the same thing as we achieved in Vue, the extra bit of writing is there because Vue essentially combines its own version of setState by default whenever a piece of data gets updated. So in short, React requires setState and then the updated data inside of it, whereas Vue makes an assumption that youâd want to do this if you were updating values inside the data object. So Why does React even bother with this, and why is setState even needed? Letâs hand this over to
âThis is because React wants to re-run certain life cycle hooks, [such as] componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate, whenever state changes. It would know that the state has changed when you call the setState function. If you directly mutated state, React would have to do a lot more work to keep track of changes and what lifecycle hooks to run etc. So to make it simple React uses setState.â
Now that we have mutations out of the way, letâs get into the nitty, gritty by looking at how we would go about adding new items to both of our To Do Apps.
How do we create new To Do Items?
React:
createNewToDoItem = () => { this.setState( ({ list, todo }) => ({ list: [ ...list, { todo } ], todo: '' }) );};
How did React do that?
In React, our input field has an attribute on it called value. This value gets automatically updated through the use of a couple of functions that tie together to create something that closely resembles two-way binding (if youâve never heard of this before, thereâs a more detailed explanation in the How did Vue do that section after this). We create this form of two-way binding by having an additional onChange event listener attached to the input field. Letâs quickly take a look at the input field so that you can see what is going on:
<input type="text" value={this.state.todo} onChange={this.handleInput}/>
The handleInput function is run whenever the value of the input field changes. It updates the todo that sits inside the state object by setting it to whatever is in the input field. This function looks as such:
handleInput = e => { this.setState({ todo: e.target.value });};
Now, whenever a user presses the + button on the page to add a new item, the createNewToDoItem function essentially runs this.setState and passes it a function. This function takes two parameters, the first being the entire list array from the state object, the second being the todo (which gets updated by the handleInput function). The function then returns a new object, which contains the entire list from before and then adds todo at the end of it. The entire list is added through the use of a spread operator (Google this if youâve not seen this beforeâââitâs ES6 syntax).
Finally, we set todo to an empty string, which automatically updates the value inside the input field.
Vue:
createNewToDoItem() { this.list.push( { 'todo': this.todo } ); this.todo = '';}
How did Vue do that?
In Vue, our input field has a handle on it called v-model. This allows us to do something known as two-way binding. Letâs just quickly look at our input field, then weâll explain what is going on:
<input type="text" v-model="todo"/>
V-Model ties the input of this field to a key we have in our data object called toDoItem. When the page loads, we have toDoItem set to an empty string, as such: todo: ââ. If this had some data already in there, such as todo: âadd some text hereâ, our input field would load with add some text here already inside the input field. Anyway, going back to having it as an empty string, whatever text we type inside the input field gets bound to the value for todo. This is effectively two-way binding (the input field can update the data object and the data object can update the input field).
So looking back at the createNewToDoItem() code block from earlier, we see that we push the contents of todo into the list arrayand then update todo to an empty string.
How do we delete from the list?
React:
deleteItem = indexToDelete => { this.setState(({ list }) => ({ list: list.filter((toDo, index) => index !== indexToDelete) }));};
How did React do that?
So whilst the deleteItem function is located inside ToDo.js, I was very easily able to make reference to it inside ToDoItem.js by firstly, passing the deleteItem() function as a prop on <ToDoItem/> as such:
<ToDoItem deleteItem={this.deleteItem.bind(this, key)}/>
This firstly passes the function down to make it accessible to the child. Youâll see here that weâre also binding this as well as passing the key parameter, as key is what the function is going to use to be able to differentiate between which ToDoItem is attempting to delete when clicked. Then, inside the ToDoItem component, we do the following:
<div className=âToDoItem-Deleteâ onClick={this.props.deleteItem}>-</div>
All I had to do to reference a function that sat inside the parent component was to reference this.props.deleteItem.
Vue:
onDeleteItem(todo){ this.list = this.list.filter(item => item !== todo);}
How did Vue do that?
A slightly different approach is required in Vue. We essentially have to do three things here:
Firstly, on the element we want to call the function:
<div class=âToDoItem-Deleteâ @click=âdeleteItem(todo)â>-</div>
Then we have to create an emit function as a method inside the child component (in this case, ToDoItem.vue), which looks like this:
deleteItem(todo) { this.$emit('delete', todo)}
Along with this, youâll notice that we actually reference a function when we add ToDoItem.vue inside of ToDo.vue:
<ToDoItem v-for="todo in list" :todo="todo" @delete="onDeleteItem" // <-- this :) :key="todo.id" />
This is what is known as a custom event-listener. It listens out for any occasion where an emit is triggered with the string of âdeleteâ. If it hears this, it triggers a function called onDeleteItem. This function sits inside of ToDo.vue, rather than ToDoItem.vue. This function, as listed earlier, simply filters the todo array insidethe data object to remove the item that was clicked on.
Itâs also worth noting here that in the Vue example, I could have simply written the $emit part inside of the @click listener, as such:
<div class=âToDoItem-Deleteâ @click=â$emit(âdeleteâ, todo)â>-</div>
This would have reduced the number of steps down from 3 to 2, and this is simply down to personal preference.
In short, child components in React will have access to parent functions via this.props (providing you are passing props down, which is fairly standard practice and youâll come across this loads of times in other React examples), whilst in Vue, you have to emit events from the child that will usually be collected inside the parent component.
How do we pass event listeners?
React:
Event listeners for simple things such as click events are straight forward. Here is an example of how we created a click event for a button that creates a new ToDo item:
<div className=âToDo-Addâ onClick={this.createNewToDoItem}>+</div>.
Super easy here and pretty much looks like how we would handle an in-line onClick with vanilla JS. As mentioned in the Vue section, it took a little bit longer to set up an event listener to handle whenever the enter button was pressed. This essentially required an onKeyPress event to be handled by the input tag, as such:
<input type=âtextâ onKeyPress={this.handleKeyPress}/>.
This function essentially triggered the createNewToDoItem function whenever it recognised that the âenterâ key had been pressed, as such:
handleKeyPress = (e) => {
if (e.key === âEnterâ) {
this.createNewToDoItem();
}
};
Vue:
In Vue it is super straight-forward. We simply use the @ symbol, and then the type of event-listener we want to do. So for example, to add a click event listener, we could write the following:
<div class=âToDo-Addâ @click=âcreateNewToDoItem()â>+</div>
Note: @click is actually shorthand for writing v-on:click. The cool thing with Vue event listeners is that there are also a bunch of things that you can chain on to them, such as .once which prevents the event listener from being triggered more than once. There are also a bunch of shortcuts when it comes to writing specific event listeners for handling key strokes. I found that it took quite a bit longer to create an event listener in React to create new ToDo items whenever the enter button was pressed. In Vue, I was able to simply write:
<input type=âtextâ v-on:keyup.enter=âcreateNewToDoItemâ/>
How do we pass data through to a child component?
React:
In react, we pass props onto the child component at the point where it is created. Such as:
<ToDoItem key={key} item={todo} />
Here we see two props passed to the ToDoItem component. From this point on, we can now reference them in the child component via this.props. So to access the item.todo prop, we simply call this.props.item.
Vue:
In Vue, we pass props onto the child component at the point where it is created. Such as:
<ToDoItem v-for="todo in list" :todo="todo" :key="todo.id" @delete="onDeleteItem" />
Once this is done, we then pass them into the props array in the child component, as such: props: [ âtodoâ ]. These can then be referenced in the child by their nameâââso in our case, âtodoâ.
How do we emit data back to a parent component?
React:
We firstly pass the function down to the child component by referencing it as a prop in the place where we call the child component. We then add the call to function on the child by whatever means, such as an onClick, by referencing this.props.whateverTheFunctionIsCalled. This will then trigger the function that sits in the parent component. We can see an example of this entire process in the section âHow do we delete from the listâ.
Vue:
In our child component, we simply write a function that emits a value back to the parent function. In our parent component, we write a function that listens for when that value is emitted, which can then trigger a function call. We can see an example of this entire process in the section âHow do we delete from the listâ.
And there we have it! đ
Weâve looked at how we add, remove and change data, pass data in the form of props from parent to child, and send data from the child to the parent in the form of event listeners. There are, of course, lots of other little differences and quirks between React and Vue, but hopefully the contents of this article has helped to serve as a bit of a foundation for understanding how both frameworks handle stuff đ¤.
If you found this useful, be sure to give lots and lots of claps đ . Hint, you can leave up to 50! đ
Github links to both apps:
Translations
But what about Angular?
Iâm glad you asked! Because the awesome Sam Borick has written Part 2 of this article!
Revisions
This article was updated on July 28th 2018, following suggestions from Dan Charousek, who kindly made recommendations for improving the Vue ToDo app, and Lucas Everett who made some fantastic suggestions for the React ToDo app (he also rewrote the createNewToDoItem and deleteItem functions) đ¤
This article was updated on July 30th 2018, to try to clarify that this article was written with new developers in mind, whether that be to either JS frameworks such as React and Vue, or simply new to the industry in general. This article is not really intended for seasoned veterans. Suggestions made by Daniel Lang and Bert Evans. Bert very kindly made a pull request to the Vue ToDo repository to implement a more robust way of emitting data back from child to parent, along with a better ID system for each ToDo Item. A quote explaining why React uses setState was also added, following a fantastic comment left by Revanth Kumar.
This article was updated on August 1st 2018, as there were some grammatical errors I had overlooked in the rush to get this article published. There was also a mistake where I had written this.props.todo, instead of this.props.item.
This article was updated on August 2nd 2018, to better explain why Vue does not require its own equivalent of setState when updating values inside the data object.
If you are interested in making this article more accessible by translating into another language, please feel free to do soâââlet me know if you do so that I can add a link to it on this page. đ
I thought Iâd also take a moment to let you know that we are currently looking for writers to join our team at Javascript In Plain English. If youâre passionate about Javascript and have a story to tell, feel free to contact us at [email protected] to find out more. đ¤