Vue for React Developers
Step by Step Component Conversion
Alex Riviere - https://alex.party
Senior Web Developer
Nexcor Technologies
@fimion@notacult.social
React is popular...
... because React is popular.
Issues
- Jobs
- There are definitely more React jobs
- Readability
- Subjective - Allowed to be wrong.
- Ecosystem/Tooling
- We don't need 80 libraries to handle styles
- Have you heard of vite?
Photo by Kyle Glenn on Unsplash
No JSX
Photo by Icons8 Team on Unsplash
babel-plugin-transform-vue-jsx
Released June 16th, 2016
Let's convert a React component
to a Vue component
Todo App
- Store an Array of todo items
- Todo items can be marked as done and will disappear
- toggle to show archived todo items
import { useState } from 'react'
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
function App() {
const [todos, updateTodos] = useState([]);
const [showArchived, updateShowArchived] = useState(false);
const addTodo = (item) => {
const myTodo = newTodo(item);
updateTodos([...todos, myTodo]);
}
const markDone = (todo)=>{
todo.done = true;
const index = todos.indexOf(todo);
todos[index] = { ...todo };
updateTodos([...todos]);
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
}
export default App
App.jsx - React Version
Let's convert it to Vue!
import { useState } from 'react'
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
function App() {
const [todos, updateTodos] = useState([]);
const [showArchived, updateShowArchived] = useState(false);
const addTodo = (item) => {
const myTodo = newTodo(item);
updateTodos([...todos, myTodo]);
}
const markDone = (todo)=>{
todo.done = true;
const index = todos.indexOf(todo);
todos[index] = { ...todo };
updateTodos([...todos]);
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
}
export default App
App.jsx - Update Imports
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
function App() {
const [todos, updateTodos] = useState([]);
const [showArchived, updateShowArchived] = useState(false);
const addTodo = (item) => {
const myTodo = newTodo(item);
updateTodos([...todos, myTodo]);
}
const markDone = (todo)=>{
todo.done = true;
const index = todos.indexOf(todo);
todos[index] = { ...todo };
updateTodos([...todos]);
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
}
export default App
App.jsx - Update Imports
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
function App() {
const [todos, updateTodos] = useState([]);
const [showArchived, updateShowArchived] = useState(false);
const addTodo = (item) => {
const myTodo = newTodo(item);
updateTodos([...todos, myTodo]);
}
const markDone = (todo)=>{
todo.done = true;
const index = todos.indexOf(todo);
todos[index] = { ...todo };
updateTodos([...todos]);
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
}
export default App
App.jsx - Update Definition
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const [todos, updateTodos] = useState([]);
const [showArchived, updateShowArchived] = useState(false);
const addTodo = (item) => {
const myTodo = newTodo(item);
updateTodos([...todos, myTodo]);
}
const markDone = (todo)=>{
todo.done = true;
const index = todos.indexOf(todo);
todos[index] = { ...todo };
updateTodos([...todos]);
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Update Definition
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const [todos, updateTodos] = useState([]);
const [showArchived, updateShowArchived] = useState(false);
const addTodo = (item) => {
const myTodo = newTodo(item);
updateTodos([...todos, myTodo]);
}
const markDone = (todo)=>{
todo.done = true;
const index = todos.indexOf(todo);
todos[index] = { ...todo };
updateTodos([...todos]);
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - New State Mechanics
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const addTodo = (item) => {
const myTodo = newTodo(item);
updateTodos([...todos, myTodo]);
}
const markDone = (todo)=>{
todo.done = true;
const index = todos.indexOf(todo);
todos[index] = { ...todo };
updateTodos([...todos]);
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - New State Mechanics
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - create a computed value
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
updateTodos([...todos, myTodo]);
}
const markDone = (todo)=>{
todo.done = true;
const index = todos.indexOf(todo);
todos[index] = { ...todo };
updateTodos([...todos]);
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Update to use array methods
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
const index = todos.indexOf(todo);
todos[index] = { ...todo };
updateTodos([...todos]);
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Update to use array methods
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
const index = todos.indexOf(todo);
todos[index] = { ...todo };
updateTodos([...todos]);
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Update marking todo as done
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Update marking todo as done
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Return a Render Function
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return ()=>(
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Return a Render Function
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return ()=>(
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Update to ref.value
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return ()=>(
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>showArchived.value = !showArchived.value)}
checked={showArchived.value} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Update to ref.value
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return ()=>(
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived.value)}
checked={showArchived.value} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Update to ref.value
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return ()=>(
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived.value)}
checked={showArchived.value} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived.value)
.map((todo, index) => (<li key={todo.id}>
{!showArchived.value && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Update to ref.value
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return ()=>(
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived.value)}
checked={showArchived.value} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived.value)
.map((todo, index) => (<li key={todo.id}>
{!showArchived.value && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Update to use a computed value
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return ()=>(
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived.value)}
checked={showArchived.value} />
Show Finished
</label>
<ul>
{filteredTodos.value.map((todo, index) => (<li key={todo.id}>
{!showArchived.value && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Update to use a computed value
What have we learned?
- You can, in fact, use JSX templating with Vue
- Vue uses reactive objects for state: ref and reactive
- you can make reactive computed values using computed
- Vue returns a render function instead of just JSX
<script>
import {ref, reactive, defineComponent, computed} from "vue"
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent({
setup() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>{
return todos.filter((e)=>e.done===showArchived.value);
})
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return {
todos,
showArchived,
addTodo,
markDone,
handleSubmit,
filteredTodos,
}
}
});
export default App
</script>
<template>
<div class="App">
<header class="App-header">
<h1>My Todo List</h1>
</header>
<form @submit="handleSubmit" class="add-item">
<label for="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type="checkbox"
v-model="showArchived">
Show Finished</label>
<ul>
<li v-for="todo in filteredTodos" :key="todo.id">
<button v-if="!showArchived" @click="markDone(todo)">
<span aria-hidden="true">✔</span>
<span class="sr-only">Mark Done</span>
</button>
{{todo.id}} - {{todo.item}}
</li>
</ul>
</div>
</template>
<style scoped src="./App.css" />
App.vue - Vue Single File Component
Recap
- React and Vue are more similar than you think.
- Use the tool you like.
- JSX is a templating language.
Thank You
More Resources
Nerando Johnson - Cassidy Williams
- Trying Vue as a React Developer - Anthony Campolo
https://prismic.io/blog/compare-react-vs-vue - Learning Vue from React - Josh Justice
https://bignerdranch.com/blog/learning-vue-from-react/ - Vue.js Docs
https://vuejs.org/
- Alex Riviere: https://alex.party
- Slides:https://slides.com/fimion/atl-dev-conf-2023/
- Twitter: @fimion
- Mstdn: @fimion@notacult.social
import { useState } from 'react'
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
function App() {
const [todos, updateTodos] = useState([]);
const [showArchived, updateShowArchived] = useState(false);
const addTodo = (item) => {
const myTodo = newTodo(item);
updateTodos([...todos, myTodo]);
}
const markDone = (todo)=>{
todo.done = true;
const index = todos.indexOf(todo);
todos[index] = { ...todo };
updateTodos([...todos]);
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return (
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived)}
checked={showArchived} />
Show Finished
</label>
<ul>
{todos.filter((e)=>e.done===showArchived)
.map((todo, index) => (<li key={todo.id}>
{!showArchived && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
}
export default App
App.jsx - React Version
/** @jsx h */
import {h, defineComponent, ref, reactive, computed} from "vue"
import './App.css'
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent(function() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>todos.filter((e)=>e.done===showArchived.value))
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return ()=>(
<div className="App">
<header className="App-header">
<h1>My Todo List</h1>
</header>
<form onSubmit={handleSubmit} className="add-item">
<label htmlFor="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type={'checkbox'}
onChange={()=>updateShowArchived(!showArchived.value)}
checked={showArchived.value} />
Show Finished
</label>
<ul>
{filteredTodos.map((todo, index) => (<li key={todo.id}>
{!showArchived.value && (<button onClick={()=>markDone(todo)}>
<span aria-hidden="true">✔</span>
<span className="sr-only">Mark Done</span>
</button>)} {todo.id} - {todo.item}
</li>))}
</ul>
</div>
)
})
export default App
App.jsx - Vue Version
<script>
import {ref, reactive, defineComponent, computed} from "vue"
let todoCount = 0;
const newTodo = (item) => ({id:++todoCount, done:false, item });
const App = defineComponent({
setup() {
const todos = reactive([]);
const showArchived = ref(false);
const filteredTodos = computed(()=>{
return todos.filter((e)=>e.done===showArchived.value);
})
const addTodo = (item) => {
const myTodo = newTodo(item);
todos.push(myTodo);
}
const markDone = (todo)=>{
todo.done = true;
}
const handleSubmit = (e) => {
e.preventDefault();
const formStuff = new FormData(e.target);
addTodo(formStuff.get('item'));
e.target.reset();
}
return {
todos,
showArchived,
addTodo,
markDone,
handleSubmit,
filteredTodos,
}
}
});
export default App
</script>
<template>
<div class="App">
<header class="App-header">
<h1>My Todo List</h1>
</header>
<form @submit="handleSubmit" class="add-item">
<label for="item">New Todo</label>
<input type="text" name="item" id="item" />
<button>ADD!</button>
</form>
<label>
<input type="checkbox"
@change="showArchived = !showArchived"
:checked="showArchived">
Show Finished</label>
<ul>
<li v-for="todo in filteredTodos" :key="todo.id">
<button v-if="!showArchived" @click="markDone(todo)">
<span aria-hidden="true">✔</span>
<span class="sr-only">Mark Done</span>
</button>
{{todo.id}} - {{todo.item}}
</li>
</ul>
</div>
</template>
<style scoped src="./App.css" />
App.vue - Vue Single File Component
Vue for React Devs - Atlanta Dev Conf
By Alex Riviere
Vue for React Devs - Atlanta Dev Conf
- 325