Intro to
for React Developers (or anyone else)
Travis Waith-Mair
@travisWaithMair
non-traditional.dev
Solid Feels Familiar to React Devs
import { render } from "solid-js/web";
import { createSignal } from "solid-js";
function Counter() {
const [count, setCount] = createSignal(0);
const increment = () => setCount(count() + 1);
return (
<button type="button" onClick={increment}>
{count()}
</button>
);
}
render(() => <Counter />, document.getElementById("app")!);
Solid uses JSX, Components and Props
function Welcome(props) {
return <p>Welcome, {props.name}</p>;
}
Solid primitives use a
"Hook-like" API
const [user, setUser] = createSignal();
createEffect(() => {
getUser(props.id)
.then(user => {
setUser(user)
})
})
Solid is not a drop-in replacement of React
Unlike frameworks like Preact, one cannot just use existing React components in a Solid Application.
Reactivity
instead of
VDOM
React VDOM
img src: https://tonynguyenit.medium.com/
Reactivity
Solid JS will surgically update only those parts of the App that are dependent on reactive values
Example
function Counter() {
const [count, setCount] = createSignal(1);
const increment = () => setCount(count() + 1);
console.log("I only get logged once")
return (
<button type="button" onClick={increment}>
{count()}
</button>
);
}
With Solid.js, it's is all about the primitives
Components in Solid "disappear" after the initial render.
Solid is built on "fine-grained" reactive primitives that do the heavy lifting of optimizing DOM updates.
Solid.js Primitives
Everything in Solid can be broken down into a Signal, a Memo, or an Effect.
createSignal
const [count, setCount] = createSignal(0);
<p>The current count is: {count()}</p>
<button onClick={() => setCount(count() + 1)}>
Increment
</button>
Signals are simply functions that return a value. They are used to keep reactive state.
createEffect
createEffect(() => {
document.title = `The current count is: ${count()}`;
});
*No Dependency Array needed
Effects let you do actions based on when signals update.
createEffect
createEffect(() => {
document.title = "I'm not reactive";
});
This effect will only run once since there are no reactive values that will trigger an update
If there are no reactive values, they don't update.
createMemo
const fullName = createMemo(() => `${firstName()} ${lastName()}`);
<p>{fullName()}</p>
It is both an effect and a signal
Works best for expensive calculations
Simple Counter Example
function Counter() {
const [count, setCount] = createSignal(0);
createEffect(() => {
document.title = `The current count is: ${count()}`;
});
return (
<div>
<p>The current count is: {count()}</p>
<button onClick={() => setCount(count() + 1)}>Plus</button>
</div>
);
}
The effect and paragraph are updated when ever the count signal is updated
A More Complicated Counter Example
function Counter() {
const [count, setCount] = createSignal(0);
const [myName, setMyName] = createSignal("Travis");
createEffect(() => {
document.title = `The current count is: ${count()}`;
});
return (
<div>
<p>The current count is: {count()}</p>
<input value={myName()} onChange={e=>setMyName(e.target.value)}/>
<button onClick={() => setCount(count() + 1)}>Plus</button>
</div>
);
}
The effect and paragraph are only updated whenever the count signal is updated, but not when the myName value is updated
Primitives don't follow React's Hook rules
export const [count, setCount] = createSignal(0);
function Counter(props) {
if(props.logCount){
createEffect(() => {
console.log(count());
});
}
return (
<div>
<p>The current count is: {count()}</p>
<button onClick={() => setCount(count() + 1)}>Plus</button>
</div>
);
}
Gotchas coming from React
Spanish
Universidad
Presidente
Embarazada?
English
University
President
Embarrassed
"False Friends" in Spanish
Solid has it's "false friends" for React Devs
Don't return early
function DataDisplay(props){
if(props.data === undefined){
return <div>Loading...</div>
}
return <div>{/* display props.data */}</div>
}
Functions "Disappear" after the initial render
function DataDisplay(props){
if(props.data === undefined){
return <div>Loading...</div>
}
/**
* This code is never run, which
* means the reactivity is never
* wired up.
*/
return <div>{/* display props.data */}</div>
}
Instead, use Control Flow components
import { Show } from "solid-js";
function DataDisplay(props){
return (
<Show when={props.data} fallback={<div>Loading...</div>}>
<div>{/* display props.data */}</div>
</Show>
);
}
Other Control Flow components like Switch, For, ErrorBoundries, and Suspense.
<Switch fallback={<div>Not Found</div>}>
<Match when={state.route === "home"}>
<Home />
</Match>
<Match when={state.route === "settings"}>
<Settings />
</Match>
</Switch>
<For
each={state.list}
fallback={<div>Loading...</div>}
>
{(item) => <div>{item}</div>}
</For>
<Suspense fallback={<div>Loading...</div>}>
<AsyncComponent />
</Suspense>
<ErrorBoundary
fallback={
(<div>
Something went terribly wrong
</div>)
}
>
<MyComp />
</ErrorBoundary>
Don't do logic outside of JSX or reactive primitives
function DoubleCounter() {
const [count, setCount] = createSignal(0);
const doubleCount = count() * 2 //NO NO
return (
<div>
<p>The double count is: {doubleCount}</p>
<button onClick={() => setCount(count() + 1)}>Plus</button>
</div>
);
}
Derive the value in a function
function DoubleCounter() {
const [count, setCount] = createSignal(0);
const doubleCount = () => count() * 2
return (
<div>
<p>The double count is: {doubleCount()}</p>
<button onClick={() => setCount(count() + 1)}>Plus</button>
</div>
);
}
OR.....
In the JSX directly
function DoubleCounter() {
const [count, setCount] = createSignal(0);
return (
<div>
<p>The double count is: {count()*2}</p>
<button onClick={() => setCount(count() + 1)}>Plus</button>
</div>
);
}
Don't destructure Props
function Greeting({salutation, name}){
return <p>{salutation}, {name}</p>
}
// or
function Greeting(props){
const {salutation, name} = props
return <p>{salutation}, {name}</p>
}
Prop values could be reactive
props = {
salutation:'Hello',
get name(){
return userName()
}
}
<Greeting salution="Hello" name={userName()}/>
Instead use props in JSX
function Greeting(props}){
return <p>{props.salutation}, {props.name}</p>
}
// default props
props = mergeProps({ name: "Smith" }, props);
// clone props
newProps = mergeProps(props);
// merge props
props = mergeProps(props, otherProps);
Solid also provides the splitProps and mergeProps utility functions.
These utilities allow you to safely split or merge props without breaking reactivity.
function MyComponent(props) {
const [local, others] = splitProps(props, ["children"]);
return (
<>
<div>{local.children}</div>
<Child {...others} />
</>
);
}
Don't use React inline style objects
function MyInlineStyle()
return (
<p
style={{
color:'blue',
backgroundColor: 'olivedrab'
}}
>
{/*...*/}
</p>
);
}
Use dash-separated property names
function MyInlineStyle()
return (
<p
style={{
color:'blue',
'background-color': 'olivedrab',
'--my-custom-property': '100px'
}}
>
{/*...*/}
</p>
);
}
or ....
Just use a string!!!!
function MyInlineStyle()
return (
<p
style={`
color: blue;
background-color': olivedrab;
--my-custom-property: 100px;
`}
>
{/*...*/}
</p>
);
}
There is no need for useRef or useCallback
import { onMount } from "solid-js";
function MyForm() {
const handleSubmit = (e) => {
/* handle submit */
};
let myInput;
onMount(() => myInput.focus());
return (
<form onSubmit={handleSubmit}>
<input ref={myInput} />
</form>
);
}
Before you give up
There is an eslint plugin
npm install --save-dev eslint eslint-plugin-solid
# or
pnpm add --save-dev eslint eslint-plugin-solid
yarn add --dev eslint eslint-plugin-solid
# optional, to create an ESLint config file
npx eslint --init
# or
pnpm eslint --init
yarn eslint --init
Why Bother Learning Solid
StateofJS 2021 results
Solid has the highest Satisfaction rating
Solid.JS is Performant by default
Solid.JS has a robust ecosystem
- Routers
- UI Components
- etc.
Shameless plug for Solid Bedrock Layout
For example, TanStack has "Solid" versions of their popular tools like TanStack-Query and TanStack Table
Solid Start Meta Framework
SolidStart enables you to render your application in different ways depending on your use case:
- Client-side rendering (CSR)
- Server-side rendering (SSR)
- Streaming SSR
- Static site generation (SSG)
Where do I go from here?
solidjs.com
Playground:
Solidjs Discord
https://discord.gg/solidjs
Thank You
Twitter: @travisWaithMair
Blog:
non-traditional.dev
Intro to Solid.js for React Developers (or anyone else)
By Justin Travis Waith-Mair
Intro to Solid.js for React Developers (or anyone else)
Solid.js has an API that feels very familiar to React developers, but under the hood is completely different. Solid.js has a suite of “fine-grained reactive primitives.” It is these primitives that we will explore and learn how to use coming from the perspective of a React.js Developer.
- 113