This article is the second part to an article I wrote previously: "A basic responsive bar chart in ReactJs can be hand-coded easily." The article discussed how to hand-code a bar chart in RecatJs. In this article, I will discuss how I rewrote the ReactJs bar chart code to svelte framework code. I outline the process I went through to re-write the React code to Svelte. You may find this useful if you have a reasonable knowledge of React and want to experiment with Svelte.
TL:DR I rewrote Reactjs code by copying over as much blocks of code I can from Recatjs to Svelte. The only bits I couldn't copy over was any code that had references to state data, but I only had to make very small adjustments for that. The final Svelte code had 80% reused code from React. My opinion is that Svelte single file components are very easy to grasp and work with especially if you are developing small app or if you are developing a prototype, and there is a great reduction in boiler plate code
You can see the full video here (All github code included in Video description):
The visual structure of the demo chart app
Below is the basic visual structure of the app, we have a legend, and SVG that houses the chart and a button. There is a function associated with the button, we will call this refreshChart(), which should generate a random set up expenses. As you can see we also need a function to return the highest expense, we will call this calculateHihestExpense().
Installation process for React and Svelte using CLI
I did the initial install of both demo apps using their respective CLI commands. See below
// For react run :
npx create-react-app chart-demo
// For Svelte run:
npx degit sveltejs/template chart-demo
For svelte you can get more info about installation here: https://svelte.dev/blog/the-easiest-way-to-get-started
Comparison of the folder structure of the source directory
After the initial setup of the app in React and in svelte, I did some cleanup and took out unnecessary code that you usually get with the boilerplate code. Above shows the simplified folder structure of our source directory, after my clean up.
The barebones code needed to bootstrap your app
The above diagram compares the barbones code/files needed to bootstrap a basic app.
Things to note about the default code that comes out the box for React and Svelte:
- Svelte - After the build process your app code will be injected into DOM object document.body
- React - After the build process your app code will be injected into DOM oject document.getElementById('root').
- Svelte - Uses single file component concept, where you name component file with extension 'svelte'. You put your js code in between script tags. Your HTML template for the component will be wrapped around a normal HTML tag. Finally, the css will be wrapped around a style tag. Basically, when you look at a svelte component file it resembles more of HTML file structure.
- React -Uses the function component concept. You write your component as a function, the name would start with capitals ie App.js. In the component file you will have the export default statement at the end to signify what is to be imported when using the import command in other areas of your app. Your template that is to be rendered will be in the form of JSX code. And last, your css is going to be imported from an external file.
Now lets fast forward to the final re-written code for our Svelte chart app.
Now lets look at the bulk of the JS code logic, in the diagram above, we have App.js (React) on the left side and App.svelte (Svelte) on the right. The green blocks of code are the code that I have copied over without any modifications. The white boxes contain code that I had made modifications to. The above image only shows the main data and logic of our app, the rendering of the view is cut off from the image, we will look at the rendering in the next section. The green blocks are mainly our initialization data and methods ie our initial expenses data (block numbered 1), general settings for our chart (block numbered 3). These green blocks numbered 1,3,4 and 6, can be copied straight to the script section in App.svelte.
The white boxes labeled 2,5 and 7 contain code that references state related data, In react, the useState hooks are used to change state data during the life cycle of our app. In svelte you are not forced to use any state management patterns and you are free to change values as you please using normal variables.
To summarise we have roughly reused more than 60% of the react JavaScript logic code. The only brain intensive task we had to do was convert the state related logic to svelte, which is quite easy, just assign values to these variables as normal. The reused code formed 90% of svelte code, so that's a great win for us too
This is how to re-write the JSX render related code to svelte
React code has all the rendering templates and logic in the form of JSX. The above diagram shows how I reconstructed the rendering logic and template in Svelte. The white boxes represent the code that I used to do this reconstruction. In Svelte , I first have a wrapper for the app, this will be a div with id of "root". As you know in our react app, the default wrapper is , so I will follow that convention so that our css styles can be copied over nicely.
The first box in the above diagram is the JSX code for the legend, this will nicely be copied over to the svelte side. Next is the refresh button, this can be converted as below:.
<button onClick={refreshChart}>Refresh Chart</button>
Converted to svelte, it becomes --->
<button on:click={refreshChart}>Refresh Chart</button>
As you can see above the click handler onClick event in react becomes on:click in svelte. This small change will enable our call back function to be called when the button is clicked.
Next element to convert is the box labelled SVG. This is the chart component that holds the JSX for the SVG element and its attributes. This can be copied over to Svelte as below :
<svg
viewBox={`0 0 ${width} ${height}`}
width="100%"
height="70%"
preserveAspectRatio="xMidYMax meet"
>
{children}
</svg>
Converted to svelte, it becomes --->
<svg
viewBox={`0 0 ${width} ${chartHeight}`}
width="100%"
height="70%"
preserveAspectRatio="xMidYMax meet"
> </svg>
Two things to note, We remove the {children} reference. This is a JSX thing, we use this pattern to construct nested components, we basically saying that this has child components. Instead we will populate it with the mark-up for the bars and text. I have also renamed the template variable ${height} to ${chartHeight}, this was minor thing that made sense in svelte.
Next we are going to reconstruct the rectangle bars and text. In the last section, we removed the reference to ${children}, in its place we are going to construct the bars and text for our chart, by modifying the JSX version as shown below:
<rect x={x} y={y} width={width} height={height} fill={ highestExpense===height ?`purple`:`black`} />
<text x={x + width / 3} y={y - 5}>
{highestExpense===height ? `${expenseName}: ${height}` : `${height}`}
</text>
Converted to svelte, it becomes --->
{#each expensesData as data,index}
<rect x={index * (barWidth + barMargin)} y={chartHeight - data.expense} width={barWidth} height={data.expense} fill={highestExpense===data.expense ? `purple` : `black`} />
<text x={index * (barWidth + barMargin) } y={chartHeight - data.expense - 5}>
{highestExpense === data.expense ? `${data.name}: ${data.expense}` : `${data.expense}`}
</text>
{/each}
Please note that the bars and text was generated by the bar component in react. But in Svelte we are going to alleviate the chart and bar component and instead we are constructing the chart with single template. In svelte, the template section has full access to our data, so for us to iterate through the data to construct the bars we use the #each looping construct, which has the following pattern:
{#each expensesData as data,index}
.....
....
{/each}
Finally the CSS styles....
we can copy the entire css styles from App.css, with an exception of the styles for body and html elements. We can place it at the end of App.svelte. You just need to remember that styles are enclosed by tag, as you would expect in HTML file. Now I mentioned that the styles for body and html elements were left out, the reason being is that App.svelte is a single file component, and the scope of the app is everything between the wrapper
. So only the styles inclusive of the div element with id=root will be styled properly. For our style sheet to target body and html elements, we need to declare it with :global(...) in our styles, as shown below::global(html){
height:100%;
padding:0;
margin:0;
font-family: 'Open Sans', sans-serif;
font-size: 2vh;;
}
:global(body) {
width:100%;
height:100%;
background-color:#ccc;
display:flex;
justify-content:center;
align-items:center;
flex-direction: column;
}
So this is what I have learned...
I was able to reuse much of the react code, the code that was written in Svelte was composed of 80% code copied straight from the React version of the app. The fact Svelte is a compiled language and that it is designed to be reactive meant that I do not have to implement any state management, this is a plus, so I can just assign new values to variables without any state management code. But I do realize that in a more complex app you do have state management requirements and there are components for that in Svelte.
In the React version of our app, we had the nested child components, Chart and Bar, to represent the nested views, this is following the React recommended pattern. The problem with this is that it produces an awful amount of code. In Svelte, this is all abstracted out. The single file component is an easy concept to grasp and it makes the code more readable. When writing the code, all you do is you write the code like if you are writing HTML code and if you have a list of items to render, svelte provides you with a handy looping block construct #each. You are sort of focusing more on the html markup and less focus on functions.
In svelte, the CSS is scoped for single file components, so I was able to reuse the style sheet from the react version. But I did have a css reference to html and body elements, which are outside of scope. To make my app style properly I had to declare out of scope elements with the :global() declaration.
Top comments (0)