Metaphysics and JavaScript
@rich_harris • Aug 2019
better
Svelte is better than React
go on, tweet this slide without context
i live for the drama
objectively
Svelte is objectively better than React
Svelte, as a compiler, results in apps that are much smaller and faster to start than traditional runtime frameworks
“Computer, Build Me an App”
JSConf EU, June 2018, Berlin
objectively
Svelte is objectively better than React
By eliminating the virtual DOM, apps become many times faster — especially critical on low-powered devices
“Rethinking Reactivity”
YGLF, April 2019, Israel
objectively
Svelte is objectively better than React
Designing a component language from first principles allows us to write leaner, clearer, more robust code
“The Return of ‘Write Less, Do More’”
JSCamp, July 2019, Barcelona
subjectively
Svelte is... subjectively better than React?
ui = f(state)
State-driven UI is easier to reason about
Pure UI
“With this model in place, the programmer is thus relieved from the burden of specifying the transition between states (or transformation) of the UI over time. No need to specify how to go from A to B: just describe what A looks like and what B looks like, in a discrete way.”
–Guillermo Rauch, rauchg.com/2015/pure-ui
Functional UI !== State-driven UI
People are horny for functions.
No man steps in the same river twice,
for it is not the same river
and he is not the same man
—Heraclitus
The Theory
of Forms
The Allegory of the Cave
<form></form>
The Ship of Theseus
ui = f(state)
ui1 = <App state={state1}/>
ui2 = <App state={state2}/>
ui1 !== ui2
So why wouldn’t we embrace this ideology?
ui = f(state)
“An ideology is a collection of normative beliefs and values that an individual or group holds for other than purely epistemic reasons.”
—Wikipedia
ideological statement
const render = state => {
document.body.innerHTML = `
<h1>Hello ${state.name}!</h1>
<input value="${state.name}">
`;
const input = document.querySelector('input');
input.oninput = () => {
render({
name: input.value
});
};
};
render({ name: 'world' });
const render = state => {
document.body.innerHTML = `
<h1>Hello ${state.name}!</h1>
<input value="${state.name}">
`;
const input = document.querySelector('input');
input.oninput = () => {
render({
name: input.value,
focus: document.activeElement === input
});
};
if (state.focus) input.focus();
};
render({ name: 'world' });
const render = state => {
document.body.innerHTML = `
<h1>Hello ${state.name}!</h1>
<input value="${state.name}">
`;
const input = document.querySelector('input');
input.oninput = () => {
render({
name: input.value,
focus: document.activeElement === input,
selectionStart: input.selectionStart,
selectionEnd: input.selectionEnd,
selectionDirection: input.selectionDirection
});
};
if (state.focus) input.focus();
input.setSelectionRange(
state.selectionStart || 0,
state.selectionEnd || 0,
state.selectionDirection || 'none'
);
};
render({ name: 'world' });
const render = state => {
document.body.innerHTML = `
<style>
h1 {
animation: fade-in 1s;
}
@keyframes fade-in {
from { opacity: 0 }
to { opacity: 1 }
}
</style>
<h1>Hello ${state.name}!</h1>
<input value="${state.name}">
`;
const input = document.querySelector('input');
input.oninput = () => {
render({
name: input.value,
focus: document.activeElement === input,
selectionStart: input.selectionStart,
selectionEnd: input.selectionEnd,
selectionDirection: input.selectionDirection
});
};
if (state.focus) input.focus();
input.setSelectionRange(
state.selectionStart || 0,
state.selectionEnd || 0,
state.selectionDirection || 'none'
);
};
render({ name: 'world' });
const App = () => {
const [name, setName] = useState('world');
return (
<>
<h1>Hello {name}!</h1>
<input onChange={e => setName(e.target.value)}>
</>
);
};
render(<App/>, document.body);
class Clock extends React.Component {
state = {
count: 0
}
componentDidMount() {
this.interval = setInterval(() => {
this.setState({
count: this.state.count + 1
});
}, 1000);
}
componentDidUpdate() {
console.log(this.state.count);
}
componentDidUnmount() {
clearInterval(this.interval);
}
render() {
return (
<p>Count: {this.state.count}</p>
);
}
}
function Clock() {
const [count, setCount] = useState(0);
// componentDidMount / componentDidUnmount
useEffect(() => {
const interval = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => {
clearInterval(interval);
};
}, []);
// componentDidUpdate
useEffect(() => {
console.log(count);
}, [count]);
return (
<p>Count: {count}</p>
);
}
—Dan Abramov, overreacted.io
it
he
No man steps in the same river twice,
for it is not the same river
and he is not the same man
—Heraclitus
dEmO tImE!!1!