articles

React for Svelte Developers

Published on November 7, 2020

Let’s say that you started learning frontend development and decided to go for that new ‘cool’ framework everyone is talking about: Svelte. Great choice! And you made some projects, and you confident of your skill and you go out looking for a job, so you got to some vacancy website and search for Svelte Jobs:

Generic Job Site

While it is getting better, right now most job openings still require React (see some thoughts about that on my previous blog post on the subject). Which sadly means you will have to learn React. There are plenty of resources out there explaining Svelte from a React developer’s point of view, so that those developers that lingered for years in the dark ages of React can finally atone and find Englightment, but very little resources the other way around, because honestly why would you ever ?

Without further ado, I would like to quickly go over the Svelte Tutorial and give for each sample there the corresponding React code so you can get started as soon as possible.

Not all of Svelte's functionality can be easily transferred to React, neither the other way around, but I hope this article will get you up with the basics of React so you can learn more on the job after you got one.

Getting Started

To start a React project (the equivalent of npx degit svelte/template my-app) you use npx create-react-app my-app, it will just like the Svelte template setup a project with the basics for a React application. The biggest differences however are

The entry point of your app will be in /src/index.js and the main component is src/App.js. This is not much different from Svelte, which has more or less exactly the same setup.

Adding data

React uses something called JSX to define it’s markup, it is kind of something like HTML but embedded into your JavaScript. At first it will feel weird and a bit outdated but you get used it. In React your component is effectively a function that returns markup and that is clearly visible in the way you write your code. In said function you can, just as in Svelte, define variables and use them inside the JSX.

import React from "react";

export default function App() {
  let name = 'world';

  return (
    <h1>Hello {name.toUpperCase()}!</h1>
  );
}

If you look carefully this code is almost exactly the same as the Svelte one and in fact for simple components you can just grab the Svelte code, slap a couple of lines of JavaScrip around it and you have a basic React component.

Dynamic Attributes

Same goes here, with some minor details, you can easily use variables for attributes.

import React from "react";

export default function App() {
  let src = 'tutorial/image.gif';
  let name = "Rick Astley"

  return (
    <img src={src} alt={`${name} dances.`} />
  );
}

Here we spot some bigger differences:

Styling

Styling for React is not done from within the component itself. The default behaviour you get from create-react-app is a setup where you can define an external App.css file that can then be imported.

import React from 'react'
import './Component.css'

export default () => <h1>Hello</h1>

However this just appends this CSS to whatever you already have and doesn’t give any scoping. That is why it is recommended to use other libraries instead. An often used library is called styled-components which allows you to do the following:

import React from 'react'
import styled from 'styled-components'

const Header = styled.h1`
  color: red;
`

export default () => <Header>Hello</Header>

In this example the component will return a h1 element with a special class applied that will turn the text red, but only for this class, this allows for scoping. For a Svelte developer it might feel wrong to have the CSS like this embedded into the JavaScript but you will eventually get used to it.

One nice advantage this has however is that because it is JavaScript and uses interpolation you can pass in properties to the styled component and use these in your CSS, this is a feature that Svelte is sadly lacking (but there are workaround for it).

import React from 'react'
import styled from 'styled-components'

const Header = styled.h1`
  color: ${props => props.color};
`

export default () => <Header color="red">Hello</Header>

Another thing worth noting here is that Header in this example is it’s own component. This means that in React you can have multiple components for each file (you could even export them as named exports) which is drastically different from Svelte.

Nested Components

Nesting components works justs like in Svelte: import them - use them.

HTML tags

This is a bit trickier as React really doesn’t like you do this it makes it obnoxiously verbose. In order to this you have to create a wrapper element that has a property dangerouslySetInnerHTML that takes an object with a property __html where you add your HTML.

import React from "react";

export default () => {
  const html = "this string contains some <strong>HTML!!!</strong>";

  return <div dangerouslySetInnerHTML={{ __html: html }} />
};

Reactivity - Assignments

Now we get to a harder part. Simple assignment just doesn’t work, this is because React consider all it’s data to be immutable because of this you have to jump through some hoops to change data in your app. The most basic way is to use a function called useState that takes an initial value as an argument and returns an array consisting of a getter and a setter.

import React, { useState } from "react";

export default () => {
  const [count, setCount] = useState(0)

  const handleClick = () => setCount(count+1)

  return (
    <button onClick={handleClick}>
      Clicked {count} {count === 1 ? 'time' : 'times'}
    </button>
  )
}

Reactive Declarations

To understand the code that follows you have to understand that in React whenever a state changes the component’s function is re-executed. So each line of code is ran again by the browser. When it encounters useState(0) it will check if this state has been used earlier and if so take the value saved else the initial value given. This means that to have the equivalent of a reactive declaration as we have them in Svelte, you can just declare a new variable on the next line:

import React, { useState } from "react";

export default () => {
  const [count, setCount] = useState(0)
  const doubled = count * 2

  const handleClick = () => setCount(count+1)

  return (<div>
    <button onClick={handleClick}>
      Clicked {count} {count === 1 ? 'time' : 'times'}
    </button>
    <p>{count} doubled is {doubled}</p>
  </div>
  )
}

In the code above the line const doubled = count * 2 will be executed every time the component renders (so it is not really a constant I guess ?). Worth noting here is also that in React you cannot return multiple tags, you have to have a root element (div in this case), this can however also be an empty tag <>...</>.

Reactive Statements

As we saw above, all our code get’s executed again when the state changes, so that would be kind of easy. For simple assignments this is fine, but we would not want an app to do a new fetch every time something changes. React also allows for a set of assignments to be executed only in specific cases called useEffect

import React, { useEffect, useState } from "react";

export default () => {
  const [count, setCount] = useState(0)
  const handleClick = () => setCount(count+1)

  useEffect(() => {
  console.log('Hello log!')
  })

  return <div>
    <button onClick={handleClick}>
      Clicked {count} {count === 1 ? 'time' : 'times'}
     </button>
  </div>
}

The above code will be executed everytime the state changes. useEffect however takes a second argument with dependencies, what exactly happens depends on what you pass:

This is a concept that takes a bit longer to get used too, and many errors and warnings in your app will come from a wrong use of the useEffect hook.

Updating arrays and object

Due to the nature of useState with it’s getter and setter this is similar to Svelte where you need an assignment to force an update.

Declaring properties

Properties in React are passed to components in the same way as in Svelte by adding them as attributes to your component.

<Component name="Stephane" designSkills="none" />

Since as we have seen a component is nothing but a function, you will find it no surprise that these props are defined as arguments to the function, the following are all possible signatures

import React from 'react'

// All props together and use props.* 
export const Component1 = (props) => <span>{props.name}</span>

// All props together and destructure in the component
export const Component2 = (props) => {
  const { name } = props
  return <span>{name}</span>
}

// Destructure props in the signature itself
export const Component3 = ({ name }) => <span>{name}</span>

// Use spreading to get all other props
export const Component3 = ({ name, ...props }) => <span>{name}</span>

Default values

To set default values for props just assign them a default value in the signature:

export const Component = ({ name = "Stephane" }) => <span>{name}</span>

Spreading props

This is obviously also possible in React.

<PersonCard {...person} />

If / else blocks

JSX is just JavaScript mixed with something that looks HTML, so it is no surprise that to create if blocks we can fall back to general JavaScript

import React from 'react'

export default ({ vaue }) => {
  return <div>
  <span>The value is {value}</span>
    {value > 10
      ? <span>This is very high</span>
      : <span>This is not enough</span>
    }
  </div>
}

Alternatively if there is no else option you can use the logical && operator to short circuit.

import React from 'react'

export default ({ vaue }) => {
  return <div>
    <span>The value is {value}</span>
    {value > 10 && <span>This is very high</span>}
  </div>
}

Each blocks

To make an each block in React you have to loop over your array and return for each item a new React node (or component), this can be easily done by leveraging the .map function

import React from 'react'

export default () => {
  let cats = [
    { id: 'J---aiyznGQ', name: 'Keyboard Cat' },
    { id: 'z_AbfPXTKms', name: 'Maru' },
    { id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
  ];

  return <div>
  <h1>The Famous Cats of YouTube</h1>

  <ul>
    {cats.map(({ id, name }, i) => (
      <li>
        <a target="_blank" href={`https://www.youtube.com/watch?v=${id}`}>
          {i + 1}: {name}
        </a>
      </li>
    )
    )}
  </ul>
  </div>
}

Keyed each blocks

This is just as important in React as in Svelte to the point that will React will warn you if you didn’t add a key. The reasons for this are very similar to those in Svelte and have to do with deciding what part of the DOM to rerender. To add a key to a list you have to add the key property. The loop above would be better rewritten as:

{cats.map(({ id, name }, i) => (
  <li key={name}>
    <a target="_blank" href={`https://www.youtube.com/watch?v=${id}`}>
      {i + 1}: {name}
    </a>
  </li>
))}

Await blocks

This does not exist in React, you will have to make your own setup using a variable that you toggle with useState

import React, { useEffect, useState } from 'react'

export default () => {
  const [data, setData] = useState([])
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    fetch(...)
      .then(res => res.json())
      .then(data => {
        setData(data)
        setLoading(false)
      })
  }, [])

  return => <div>
    {loading 
       ? <span>We are loading your data</span>
       : <span>{data}</span>
    }
  </div>
}

DOM Events

To attach events to your DOM elements you use the same technique as in Svelte by simply assigning a function to it, with the difference that it uses camelCase instead of the on:* notation, this can of course be either a function reference or an inline handler

<button onClick={handleClick}>Click me</button>
<button onClick={() => setCount(count + 1)}>Click me</button>

Event modifier

React does not have build in support for event modifiers, you will have to set that up yourself.

Component events

To raise events from components React uses old fashioned callbacks, just define a callback as a property and call it whenever required.

Bindings

React does not believe in two way binding, in order to do this you have to pass through useState with it’s getter and setter.

Lifecycle onMount & onDestroy

We actually already have seen this, in React you can use the useEffect with an empty dependency array to simulate an onMount function

import React, { useEffect } from 'react'

export default () => {
  useEffect(() => {
    console.log('the componented mounted')

    return () => console.log('the componented unmounted')
  }, [])

  return <h1>This is a text</h1>
}

The above code also immediatley shows you how to do onDestroy, simply return a function from the useEffect you used for the onMount.

beforeUpdate / afterUpdate / tick

As far as I know, React doesn’t have these (I might be mistaken)

Stores / Motion / Animation / Transitions

This is not implemented natively in React.

Component Composition

In React all components take a property children that gives you the children of that component. You can consider this as if each component already has slots build in. One nice advantage of this is that if the children are an array (if you have more than one) you can count them, loop over them, manipulate them, wrap each child individually. Some of these are not possible in Svelte as of now.

getContext / setContext

This is a bit more of an advanced concept and this article is starting to get long already. React has something called useContext that once you get comfortable with all the rest is surely worth checking out. It can be hard to grasp in the beginning but once you understand how it works will power a lot of your apps.

Next Steps

Congratulations, you read this far!!

With all the above information, you should be set for a quick start in the world of React. Some information is missing, some is not 100% correct or simplified but it will be a good beginning, the React community is very large as it is a popular framework and there are daily new articles and tutorials written about by people far more proficient in it than me.