In the previous article I introduced myself into Svelte and explained some of the basics from a React developer's perspective. Today, I'm getting into it more in-depth and will explore how to derive values from state and how to pass props to child components while comparing Svelte's approach with React's.
Computed Values
Svelte calls these Reactive Declarations. With some special syntax, you can declare variables whose values will be derived from other variables. Let's say you have variable b whose value is derived from what variable a contains. Each time the value of a changes, the value of b will be recomputed.
The way of writing Reactive Declarations is by using a $: before the variable's declaration.In the following example, we compute or derive the variable age from the variable birthyear and each time we change the value of birthyear via the input element, the value of age will also be updated.
<script>
let birthyear = 1993;
let currentYear = new Date().getFullYear();
$: age = currentYear - birthyear;
function handleChange(e) {
birthyear = e.target.value;
}
</script>
<input
type="number"
id="birthyear"
name="birthyear"
min="1960" max="2020"
value={birthyear}
on:change={handleChange}
>
<p>Your age is {age}</p>
Reactive Declarations are useful when you want to reuse them in several places, so you don't have to do the computations over and over again.
Reactive Statements
Following the previous approach, you can also use Reactive Statements and put the $: before things like if or console.log statements. This can be useful if you only want to execute stuff if your variable in the state meets certain criteria.
Anywhere inside your script block you can do things like:
<script>
// Single statment
$: console.log(`Logging the value of ${age}`);
// Grouped statements
$: {
alert(`Your year of birth is: ${birthyear}`);
console.log(`Your age is: ${age}`);
}
// Conditional
$: if (age <= 20) {
console.log('Hey you are still young!')
}
// Conditional Re-Setting Value
$: if (age < 18) {
alert(`People less than 18 y/o are not allowed`);
age = 18;
}
</script>
Coming from React you may think all this may be unnecessary, given the fact that when working with React, if you want to have computed or derived values you just assign a computation to a variable. Depending on where you declare this computation it will run each time the state or the props change, or you can have it in a function that you can call whenever you want. All this without additional syntax.
Still, Svelte keeps this pretty simple and easy to remember so this shouldn't be a big obstacle.
Updating Arrays and Objects
React's rule of thumb is that you do not mutate the state directly. You have to make use of React's setState or hooks. By using them you make sure your state updates accordingly and your components react and re-render.
Svelte works the same way behind the scenes, although you don't have any setState or hooks.If you have an array and you push a value to it, there won't be any state update, unless you make a reassignment of that array with the updated value.
I see this re-assignment as our setState from React because it is this re-assignment that triggers the reactivity in Svelte.
As mentioned in the Svelte tutorial:
Svelte's reactivity is triggered by assignments.
I took the example from the tutorial and commented it to make this easier to understand:
<script>
let numbers = [1, 2, 3, 4];
function addNumber() {
// We push a new value to the array
// this won't trigger any state update
numbers.push(numbers.length + 1);
// We re-assign the updated array to the same variable
// I see this as our setState or hook setter from React
// Without this, there're no state updates
numbers = numbers;
// Instead of all this you can also do just
// numbers = [...numbers, numbers.length + 1];
}
</script>
<p>{numbers.join(' + ')}</p>
<button on:click={addNumber}>
Add a number
</button>
When working with objects it gets kind of tricky because you have to understand how references work in Javascript. When assigning an object to a variable, this variable doesn't get a new copy of that object, instead, it gets a reference to the original object. This means that if you mutate a property on the newly declared one, it will also mutate the property from the original object.
Having said that, still after the mutation Svelte won't know that the state has to be updated, unless you make an assignment.
Again, a commented example would make this easier to understand:
<script>
let obj = {
foo: {
bar: 'bar'
}
}
function updateProperty() {
// foo has a reference to obj.foo
const foo = obj.foo;
// Mutating foo.bar also mutates obj.foo.bar
foo.bar = 'baz';
// But you need a re-assignment of 'obj'
// So Svelte updates the state accordingly
obj = obj;
}
</script>
<p>
{obj.foo.bar}
</p>
<button on:click={updateProperty}>
Update Property
</button>
Now, the following also worked for me, by directly assigning the new value to obj.foo.bar.
function updateProperty() {
obj.foo.bar = 'baz';
}
Props
We're finally talking about properties or just props. Just like React, we can pass properties down to our child components and the concept is the same. Svelte does it in a slightly different way tho.
In the next example, we are going to import a Child component and we're gonna pass a property called age to it. So far this is very similar to React.
<script>
import Child from './Child.svelte';
</script>
<Child age={10}/>
Now, there's a difference in how this looks on the Child component. In React, you receive the props via the parameters in a functional component or via this.props in a class component and that's pretty much it.
In Svelte tho, this is how it would look on the Child component:
<script>
export let age;
</script>
<p>My age is {age}</p>
At first glance, this doesn't make sense. Why would we want to use export when we want to use a property we are receiving from our parent component? Well, although it doesn't make sense from a semantical standpoint my interpretation is that we are exposing the property to the rest of our component so we can use it in our elements below.
Default Props
We can specify default values for the props in the Child component just like this:
<script>
export let age = 0;
</script>
This way, if the component doesn't receive the property we can show a value instead of undefined. This is the same as React's defaultProps but with a little bit of less code.
Spread Props
And last but not least, we can use our friend the spread operator when we want to pass several properties to our children.
So instead of doing this:
<User name={userInfo.name} age={userInfo.age} email={userInfo.email}/>
We can do the following:
<script>
import User from './User.svelte';
const userInfo = {
name: 'José',
age: 27
email: 'jose@email.com'
};
</script>
<User {...userInfo}/>
This is all for the second part, there's still a lot of topics to cover but I'll work on those during the following weeks. Thanks for reading and stay tuned!
Follow me on twitter: @jdelvx
Top comments (2)
Thanks for the article! I think in the last part you forgot replacing the individual props with the actual spread:
You're right! I'm gonna change it. Thanks a lot!