Props up with Svelte
Thomas Collardeau
Posted on October 7, 2019
Passing props down (React)
Let's present the paradigm by creating a small Counter component in React first:
export default function Counter() {
const [count, setCount] = useState(0);
const incr = () => setCount(c => c + 1);
return (
<div>
<div>{count}</div>
<button onClick={incr}>+1</button>
</div>
);
}
And let's use it an App component:
import './Counter';
function App() {
return (
<Counter />
)
}
Now, let us imagine that we need to know if the count is more than 1 in our App
component (maybe we need to pluralize a string if that's the case).
Mmh... we don't have access to count
from App
. We have to lift the state up and pass it back down as a prop to <Counter />
. As a result, we also need to pass down the increment function to update said count
. Here is an implementation:
// App.js
import Counter from './Counter';
function App() {
// state is now in the parent
const [count, setCount] = useState(0);
const incr = () => setCount(c => c + 1);
return <Counter count={count} incr={incr} />;
}
// Counter.js
export default function Counter(props) {
// the child uses props
const { count, incr } = props;
return (
<div>
<div>{count}</div>
<button onClick={incr}>+1</button>
</div>
);
}
Nice, it works (sandbox). Yet it's slightly odd to me that our Counter
component has become a shell of itself (it seems). It doesn't do any counting anymore. It wires in a click handler. Maybe we'd have to rename it? But I digress.
Component Bindings (Svelte)
Let's try to handle the same issue in Svelte. Here is our Counter
:
<script>
let count = 0;
const incr = () => (count = count + 1);
</script>
<div>{count}</div>
<button on:click={incr}>+1</button>
And a parent component, App
:
<script>
import Counter from './Counter.svelte';
</script>
<Counter />
So we're back in the same situation: we want to use count
from Counter
in the App
component. We could lift state up again as before. But this time, we can actually pass props up without having to re-wire any state. We need to:
1/ export
the variable in the Counter
(child) component:
export let count = 0
2/ bind to the component value in the App
(parent) component
<script>
let count;
</script>
< Counter bind:count />
We declare the variable in the script
tag, and we grab it via component bindings (bind:count
). So, we have access to the count in the parent component! Let's actually solve the problem in full, using reactive declarations as well.
App.svelte
:
<script>
import Counter from './Counter.svelte';
let count;
$: isMoreThan1 = count > 1;
</script>
<Counter bind:count />
<span>is More than 1: {isMoreThan1} </span>
Counter.svelte
:
<script>
export let count = 0;
const incr = () => (count = count + 1);
</script>
<div>{count}</div>
<button on:click={incr}>
+1
</button>
I'm interested in your thoughts. What do you think are the repercussion for componentization in Svelte coming from React? Is there a similar mechanism in Vue or Angular?
Give me a follow on Twitter for more explorations with Svelte :)
Posted on October 7, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.