What is Solidjs?
Today when I am surfing on internet searching for some highly performant frameworks for frontend, I found solidjs, which is a simple and performant reactive building user interfaces.
the project github link is here
Begin
while we can start using a vite-plugin-solid:
npx degit solidjs/templates/js my-solid-project
cd my-solid-project
npm install # or pnpm install or yarn install
npm run start # starts dev-server with hot-module-reloading
npm run build # builds to /dist
then you can manually install vite related plugins :
# with npm
npm install -D vite vite-plugin-solid babel-preset-solid
npm install solid-js
# with pnpm
pnpm add -D vite vite-plugin-solid babel-preset-solid
pnpm add solid-js
# with yarn
yarn add -D vite vite-plugin-solid babel-preset-solid
yarn add solid-js
after installing all those you will find in vite.config.js
as
// vite.config.ts
import { defineConfig } from 'vite';
import solidPlugin from 'vite-plugin-solid';
export default defineConfig({
plugins: [solidPlugin()],
});
the code I use for some demo is here:
import logo from "./logo.svg";
import styles from "./App.module.css";
import {
createEffect,
createSignal,
createMemo,
createResource,
Show,
Switch,
Match,
} from "solid-js";
import { render, Portal, Dynamic } from "solid-js/web";
const fetchUser = async (id) =>
(await fetch(`https://swapi.dev/api/people/${id}/`)).json();
const User = () => {
const [userId, setUserId] = createSignal();
const [user] = createResource(userId, fetchUser);
return (
<>
<input
type="number"
min="1"
placeholder="Enter Numeric Id"
onInput={(e) => setUserId(e.currentTarget.value)}
/>
<span>{user.loading && "Loading..."}</span>
<div>
<pre>{JSON.stringify(user(), null, 2)}</pre>
</div>
</>
);
};
function fibonacci(n) {
if (n <= 1) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
function Modal() {
return (
<div class="container">
<Portal>
<div class="popup">
<h1>Popup</h1>
<p>Some text you might need for something or other.</p>
</div>
</Portal>{" "}
</div>
);
}
function Compare() {
const [x] = createSignal(7);
return (
<Switch fallback={<p>{x()} is between 5 and 10</p>}>
<Match when={x() > 10}>
<p>{x()} is greater than 10</p>
</Match>
<Match when={5 > x()}>
<p>{x()} is less than 5</p>
</Match>
</Switch>
);
}
function Cats() {
// <For each={}> to handle for loop, it updates or moves DOM rather than recreating them
const [cats, setCats] = createSignal([
{ id: "J---aiyznGQ", name: "Keyboard Cat" },
{ id: "z_AbfPXTKms", name: "Maru" },
{ id: "OUtn3pvWmpg", name: "Henri The Existential Cat" },
]);
return (
<For each={cats()}>
{(cat, i) => (
<li>
<a
target="_blank"
href={`https://www.youtube.com/watch?v=${cat.id}`}
style={{ color: "white" }}
>
{i() + 1}: {cat.name}
</a>
</li>
)}
</For>
);
}
// wrong naming would not work here
function Btn() {
const [loggedIn, setLoggedIn] = createSignal(true);
const toggle = () => setLoggedIn(!loggedIn());
return (
<Show when={loggedIn()} fallback={<button onClick={toggle}>Log in</button>}>
<button onClick={toggle}>Log out</button>
</Show>
);
}
function Counter() {
const [count, setCount] = createSignal(0);
const fib = createMemo(() => fibonacci(count()));
const doubleCount = () => count() * 2;
createEffect(() => {
console.log("the count is: ", count());
});
return (
<>
<button onClick={() => setCount(count() + 1)}>Click Me</button>
{count()} <br />
{doubleCount()} <br />
fib: {fib()}
</>
);
}
function App() {
return (
<div class={styles.App}>
<p>Counter demos how signal and createEffect / createMemo work</p>
<Counter /> <p>Btn demos how simple conditional works in Solid</p>
<Btn />
<p>Cats demos how list rendering works using For tag </p>
<Cats />
<p>Compare demos how switch and match work</p>
<Compare />
<p>Modal demos how Portal works </p>
<Modal />
<p>Async demos </p>
<User />
</div>
);
}
export default App;
let's take a look one by one, this article will update
Entry and Components
- one thing I like solidjs is that
class
isclass
instead ofclassName
, and the most of others are the same to react jsx writing - solidjs uses
render()
function to render, takes 2 args, first one is a function, second one is a container
Signal and Effect / Memo
signals
are the most basic reactive primitive in solidjs
, they track a single value, which can be any JS object that changes over time.
the createSignal
function returns a pair of functions as a two-element array: a getter and a setter, basically remind me of useState
here we define a Counter
, we define count
as a signal, and two derived signals from it, that is fib
and doubleCount
let's show how reactivity works in solidjs here, clicking the button to add number, and both doubleCount
and fib
will update
function Counter() {
const [count, setCount] = createSignal(0);
const fib = createMemo(() => fibonacci(count()));
const doubleCount = () => count() * 2;
createEffect(() => {
console.log("the count is: ", count());
});
return (
<>
<button onClick={() => setCount(count() + 1)}>Click Me</button>
{count()} <br />
{doubleCount()} <br />
fib: {fib()}
</>
);
}
createEffect
in solidjs is used to create a effect as a function
the effect automatically subscribes to any signal that is read during the function's execution and returns when any of them changes --> kinda like Vue's watch
Flow, Show
JSX allows you to use JS to control the logic flow in the templates, but when virtual DOM is not in use here, use of Array.prototype.map
would wastefully recreate all the DOM nodes on every update
for a simple conditional display, solidjs provides a Show
tag:
<Show
when={loggedIn()}
fallback={<button onClick={toggle}>Log in</button>}
>
<button onClick={toggle}>Log out</button>
</Show>
fallback
prop acts as else
and show when the condition passed to when
is not true
example here:
// wrong naming would not work here
function Btn() {
const [loggedIn, setLoggedIn] = createSignal(true);
const toggle = () => setLoggedIn(!loggedIn());
return (
<Show when={loggedIn()} fallback={<button onClick={toggle}>Log in</button>}>
<button onClick={toggle}>Log out</button>
</Show>
);
}
For, Index, Switch, Match
coming to more complex conditionals, we need its provided
to render a list, due to the reason I mention above (no virtual DOM here), solidjs also provides For
tag here
in this example we will render a list of index with its content
function Cats() {
// <For each={}> to handle for loop, it updates or moves DOM rather than recreating them
const [cats, setCats] = createSignal([
{ id: "J---aiyznGQ", name: "Keyboard Cat" },
{ id: "z_AbfPXTKms", name: "Maru" },
{ id: "OUtn3pvWmpg", name: "Henri The Existential Cat" },
]);
return (
<For each={cats()}>
{(cat, i) => (
<li>
<a
target="_blank"
href={`https://www.youtube.com/watch?v=${cat.id}`}
style={{ color: "white" }}
>
{i() + 1}: {cat.name}
</a>
</li>
)}
</For>
);
}
Switch
and Match
are used when we have to deal with conditionals with more than 2 mutual exclusive outcomes:
function Compare() {
const [x] = createSignal(7);
return (
<Switch fallback={<p>{x()} is between 5 and 10</p>}>
<Match when={x() > 10}>
<p>{x()} is greater than 10</p>
</Match>
<Match when={5 > x()}>
<p>{x()} is less than 5</p>
</Match>
</Switch>
);
}
well if you want to write tight and clean code, we can use Dynamic
to write
<Switch fallback={<BlueThing />}>
<Match when={selected() === 'red'}><RedThing /></Match>
<Match when={selected() === 'green'}><GreenThing /></Match>
</Switch>
# to this
<Dynamic component={options[selected()]} />
ErrorBoundary, Suspense
well you can capture js error in the UI and fallback to what you want to display, first, define a <Broken />
:
const Broken = (props) => {
throw new Error("Oh No");
return <>Never Getting Here</>
}
and then use it:
<div>Before</div>
<ErrorBoundary fallback={err => err}>
<Broken />
</ErrorBoundary>
<div>After</div>
Lifecycle (onMount, onCleanup, onError)
solidjs has few lifecycle APIs here
onMount(async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);
setPhotos(await res.json());
});
const timer = setInterval(() => setCount(count() + 1), 1000);
onCleanup(() => clearInterval(timer));
Conclusion
well, solidjs
looks interesting, but it seems like Vue3 can do what it sells too, with much richer ecology and other stuff. I personally still stick to Vue.
Top comments (0)