SolidJS

1. SolidJS is what

A declarative, efficient, and flexible JavaScript library for building user interfaces

2. Why use SolidJS

  1. Close to native, good performance, fast speed

  2. The packet capacity is small and contains all core contents except routing

  3. The syntax is simple and flexible, using JSX(not VDOM), similar to React Hooks syntax

Introduction to 3.

The basic scaffold template can be obtained with the solidJS command

#1. Create a vm directly
> npx degit solidjs/templates/js my-app
> cd my-app
> npm i # or yarn or pnpm
> npm run dev # or yarn or pnpm

#2. Use degit to create a file
> npm i -g degit
> degit solidjs/templates/js my-app
> cd my-app
> npm i # or yarn or pnpm
> npm run dev # or yarn or pnpm

#3. Use TypeScript or degit
> npx degit solidjs/templates/ts my-app
> cd my-app
> npm i # or yarn or pnpm
> npm run dev # or yarn or pnpm
Copy the code

We can use SolidJs as a plugin, as a third party library, and also use SolidJs functions

> npm install solid-js
Copy the code

3.1 Entry and Components

  • This is the same as the React component and can accept the props object and return the JSX element. The first parameter of the component accepts the PROPS object and returns the real DOM node. All JSX nodes are real DOM
  • Solidjs JSX differs from React JSX in one significant way. Solidjs defaults to and will never use class components, so there is no conflict with the class keyword. The SolidJS class selector is class instead of React className
  • SolidJS uses the Render function to render, which takes two arguments, the first as a function and the second as the mounted container, and returns a destruction method
import { render } from 'solid-js/web'
const App = () = > {
    return (
     <div class="title">Hello, SolidJS<div>
    )
}

render(() => <App />, document.getElementById('root'))
Copy the code

3.2 Props

  • Solid allows you to define properties on components that pass data to child components
import { createSignal } from 'solid-js'
const Parent = () = > {
    const [greeting, setGreeting] = createSignal("Hello")
    return (
      <div>
        <Label greeting="greeting">
          <div>Jhon</div>
        </Label>
      </div>)}const Label = (props) = > {
    <>
      <div>{ props.greeting }</div>
   	  { props.children }
    </>
}
Copy the code
  • Unlike other frameworks, you can’t build components inpropsObject destruct is used on. This is becausepropsThe object relies on the object’s getter to lazily obtain its value. Using object deconstruction breaks the responsiveness of props.
// Here 'props. Name' will be updated as you want
const MyComponent = props= > <div>{props.name}</div>;

// Wrong way
// Here, 'props. Name' is not updated (that is, not reactive) because it is deconstructed as' name '
const MyComponent = ({ name }) = > <div>{name}</div>;
Copy the code
  • While the props object looks like a normal object when used (Typescript users will notice that it is typed like a normal object), it is actually reactive — using Proxy to implement reactive, deconstruction takes away the Proxy nature of the value

3.3 Signal

  • Signal is the core responsive basic. To create a Signal, you can use thesolid-jsThe importcreateSignal
  • The parameters passed to the createSignal call are the initial values, and createSignal returns an array of two functions, one getter and one setter
  • The first return value is a getter rather than the value itself, which needs to be accessed through a function call
import { createSignal } from "solid-js"

function Counter() {
  const [count, setCount] = createSignal(0)
  return (
  	<div>
    	<div>Count: 0</div>
    	<button onClick={()= > setCount((c) => c + 1)}>add</button>
    </div>)}Copy the code

3.4 Effect

  • Effect is an observer, created with createEffect, that receives a function and monitors its execution
  • createEffectAutomatically subscribes to all signals read during execution and reruns the function if one of those Signal values changes
  • createEffectThe created Effect is run after rendering is complete and is primarily used to schedule updates that interact with the DOM after rendering
import { createSignal, createEffect } from 'solid-js'
import { render } from 'solid-js/web';

export default function App() {
  const [count, setCount] = createSignal(0)
  createEffect(() = > {
    console.log('The count is now', count())
  })
  return (
    <>
      <div>{count()}</div>
      <button onClick={()= > setCount(c => c + 1)}>add</button>
    </>
  )
}

render(() = > <Counter />.document.getElementById('app'))
Copy the code

3.5 Memo

  • We can use the Memo to cache values to reduce rework
  • Memo (a special primitive) stores and accesses the last cached values without re-evaluating them until their dependencies change
# 1.Every time fib is called, it is reexecutedimport { render } from 'solid-js/web'
import { createSignal } from 'solid-js'

function fibonacci(num) {
  if (num <= 1) return 1
  return fibonacci(num - 1) + fibonacci(num - 2)}function Counter() {
  const [count, setCount] = createSignal(10)
  const fib = () = > {
      console.log(The value has changed.)
      return fibonacci(count())
  }

  return (
    <>
      <button onClick={()= > setCount(count() + 1)}>Count: {count()}</button>
      <div>1. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>2. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>3. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>4. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>5. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>6. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>7. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>8. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>9. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>10. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
    </>
  )
}

render(() = > <Counter />.document.getElementById('app'))

Copy the code

# 2.Each call to fib after memo # meme processing is not reexecuted as long as the value has not changedimport { render } from 'solid-js/web'
import { createSignal, createMemo } from 'solid-js'

function fibonacci(num) {
  if (num <= 1) return 1

  return fibonacci(num - 1) + fibonacci(num - 2)}function Counter() {
  const [count, setCount] = createSignal(10);
  const fib = createMemo(() = > {
  console.log("The value has changed");
  return fibonacci(count());
})

  return (
    <>
      <button onClick={()= > setCount(count() + 1)}>Count: {count()}</button>
      <div>1. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>2. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>3. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>4. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>5. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>6. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>7. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>8. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>9. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
      <div>10. {fib()} {fib()} {fib()} {fib()} {fib()}</div>
    </>
  )
}

render(() = > <Counter />.document.getElementById('app'))


Copy the code

4. Process control

  • Solidjs does not have a virtual DOM and uses things likeArray.prototype.mapSomething like that would wastefully recreate all the DOM nodes with each update. Conversely, it is common for Reactive libraries to use template tools
  • Solidjs templates are: Show, For, Switch/Match, Index, Dynamic, Portal, ErrorBoundary

4.1 Show

# 1.Using triples (a? B: c) and Boolean expressions (a && b) can be used as basic conditional controls. However, using the <Show> component is usually more concise as the # fallback attributeelse, the condition passed to when is nottrueDisplayed whenimport { render } from 'solid-js/web'
import { createSignal, Show } from 'solid-js'

function App() {
  const [loggedIn, setLoggedIn] = createSignal(false)
  const toggle = () = >setLoggedIn(! loggedIn())return (
    <>
      <Show
        when={loggedIn()}
        fallback={()= > <button onClick={toggle}>Log in</button>}
        >
        <button onClick={toggle}>Log out</button>
        </Show>
    </>
  )
}

render(() = > <App />.document.getElementById('app'))
Copy the code

4.2 the For

  • <For>Components are the best way to iterate through any array of non-raw values by referring to automatic key references so that when the data is updated it can be optimized to update or move rows rather than recreate them
  • indexIs a Signal, so it can be updated independently as rows are moved
  • The data item is not Signal because a change would mean a new reference and cause a new row to be created. Nested updates can be done by creating a nested Signal or using Solid’s Store agent
import { render } from 'solid-js/web'
import { createSignal, For } from 'solid-js'

function App() {
  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>{i() + 1}: {cat.name}</a>
        </li>
      )}
  	</For>
  )
}
render(() = > <App />.document.getElementById('app'))


Copy the code

4.3 the Index

  • When dealing with raw values or two-dimensional arrays, treating values as keys can lead to a lot of unnecessary rendering
  • The array Index in Index is the actual key of the list
  • <Index><For>Has similar signatures except that the data item is Signal and the index is fixed

import { render } from 'solid-js/web'
import { createSignal, Index } from 'solid-js'

function App() {
  const [cats, setCats] = createSignal([1.2.3])
  
  return (
    <Index each={cats()}>
    {(cat, i) => (
      <div>
        <div>
          {i + 1}: {cat()}
        </div>
      </div>
    )}
  </Index>
  )
}

render(() = > <App />.document.getElementById('app'))

Copy the code

4.4 the Switch/Match

  • Switch is used to handle more than two mutually exclusive conditions
  • Try to match each condition and stop rendering when the first condition evaluates to true. If all else fails, it will render the content fallback.
import { render } from "solid-js/web"
import { createSignal, Switch, Match } from "solid-js"

function App() {
  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>
  )
}

render(() = > <App />.document.getElementById("app"))

Copy the code

4.5 the Dynamic

  • <Dynamic>Tags are useful when dealing with rendering from data.<Dynamic>Lets you pass the element’s strings or component functions to it and render the component using the remaining props provided
import { render, Dynamic } from "solid-js/web"
import { createSignal, For } from "solid-js"

const RedThing = () = > <strong style="color: red">Red Thing</strong>
const GreenThing = () = > <strong style="color: green">Green Thing</strong>
const BlueThing = () = > <strong style="color: blue">Blue Thing</strong>

const options = {
  red: RedThing,
  green: GreenThing,
  blue: BlueThing
}

function App() {
  const [selected, setSelected] = createSignal("red");

  return (
    <>
      <select value={selected()} onInput={e= > setSelected(e.currentTarget.value)}>
        <For each={Object.keys(options)}>{
          color => <option value={color}>{color}</option>
        }</For>
      </select>
      <Dynamic component={options[selected()]} />
    </>
  )
}

render(() = > <App />.document.getElementById("app"))
Copy the code

4.6 Portal

  • Sometimes it is useful to insert elements out of the normal order of an application. Z-index is sometimes inadequate to handle render contexts such as modal box class floating elements
  • Solid provides a<Portal>Components,<Portal>Is inserted at the selected location. By default, its elements will be indocument.bodyUnder the<div>In the present
import { render, Portal } from "solid-js/web"

function App() {
  return (
    <div class="app-container">
      <p>Just some text inside a div that has a restricted size.</p>
      <Portal>
        <div class="popup">
            <h1>Popup</h1>
            <p>Some text you might need for something or other.</p>
        </div>
       </Portal>
    </div>
  );
}

render(() = > <App />.document.getElementById("app"))
Copy the code

4.7 ErrorBoundary

  • An ErrorBoundary is a component tree that captures JavaScript errors generated anywhere in the subcomponent tree. The ErrorBoundary records these errors and displays a component tree that backs up the UI instead of crashing

import { render } from "solid-js/web"
import { ErrorBoundary } from "solid-js"

const Broken = (props) = > {
  throw new Error("Oh No")
  return <>Never Getting Here</>
}

function App() {
  return (
    <>
      <div>Before</div>
      <ErrorBoundary fallback={(err)= > err}>
        <Broken />
      </ErrorBoundary>
      <div>After</div>
    </>
  )
}
render(() = > <App />.document.getElementById("app"))
Copy the code

4.8 Suspense

  • <Suspense>Is a component that keeps track of all read resources under it and displays fallback placeholder status until they are parsed.SuspenseShowThe difference is that it is non-blocking, and both branches can exist at the same time even if they are not currently in the DOM
<Suspense fallback={<div>Loading...</div>} ><AsyncComponent />
</Suspense>
Copy the code

5. Life cycle

  • There are only a few life cycles in Solid because all survival destruction is controlled by the responding system. The response system is created and updated simultaneously, so the only scheduling is to write the logic to the Effect where the update ends

5.1 onMount

  • OnMount is an alias for a non-trace (never rerun) state of createEffect
  • It’s just an Effect call that runs once in the component once all the initial rendering is done
import { render } from "solid-js/web"
import { createSignal, onMount, For } from "solid-js"
import "./styles.css"

function App() {
  const [photos, setPhotos] = createSignal([])

  onMount(async() = > {const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`)
    setPhotos(await res.json())
  });

  return <>
    <h1>Photo album</h1>

    <div class="photos">
      <For each={photos()} fallback={<p>Loading...</p>}>{ photo =>
        <figure>
          <img src={photo.thumbnailUrl} alt={photo.title} />
          <figcaption>{photo.title}</figcaption>
        </figure>
      }</For>
    </div>
  </>
}

render(() = > <App />.document.getElementById('app'))
Copy the code

5.2 onCleanup

  • Is triggered when destruction or recalculation is performed in the current response scope. Can be used with any component or Effect

import { render } from "solid-js/web"
import { createSignal, onCleanup } from "solid-js"

function Counter() {
  const [count, setCount] = createSignal(0)

  const timer = setInterval(() = > setCount(count() + 1), 1000)
  onCleanup(() = > clearInterval(timer))

  return <div>Count: {count()}</div>
}

render(() = > <Counter />.document.getElementById('app'))
Copy the code

5.3 onError

  • Registers an error handler that executes when a child scope throws an error. But the nearest range error handler is executed. A rethrow can trigger upwards

6. The binding

6.1 event

  • The event properties in Solid will be displayed asonThe prefix
  • Common UI events (bubbling and composition) are automatically delegated to document elements. To improve delegate performance, Solid supports array syntax for invoking event handlers, and we use data as a second argument so there is no need to create additional closures
  • allonBindings are case insensitive, which means event names need to be lowercase. This can be used if you need to support other case or do not use event delegateon:Namespace to match the event handler after the colon
# 1.Basic writingimport { render } from "solid-js/web"
import { createSignal } from "solid-js"

function App() {
  const [pos, setPos] = createSignal({x: 0.y: 0})

  function handleMouseMove(event) {
    setPos({
      x: event.clientX,
      y: event.clientY
    })
	}

  return (
    <div onMouseMove={handleMouseMove}>
    The mouse position is {pos().x} x {pos().y}
    </div>
  )
}

render(() = > <App />.document.getElementById('app'))
Copy the code
# 2.Array syntax callimport { render } from "solid-js/web"
import { createSignal } from "solid-js"

function App() {
  function handleMouseMove(data) {
    console.log("data", data)
	}

  return (
    <div onMouseMove={[handleMouseMove, "data"]} ></div>
  );
}

render(() = > <App />.document.getElementById('app'))
Copy the code

6.2 style

  • The style property in Solid accepts a style string or object
  • Solid callsstyle.setPropertyFor style setting. This means that keys need to take the form of dashes, as inbackground-colorRather thanbackgroundColor. But that means we can set CSS variables
  • classListAccepts an object where the key is the class name and the value is a Boolean expression. This class is applied when true, and removed when false.
  • classlistThe dynamic class in Vue accepts an object
<div style={{ "--my-custom-color": themeColor() }} />

<div
  style={{
    color: `rgb(${num()}, 180, ${num()})`,
    "font-weight": 800,"font-size":` ${num()}px`,}} >
  Some Text
</div>


<button
  classList={{ selected: current() = = ="foo" }}
  onClick={()= > setCurrent("foo")}
>
  foo
</button>

Copy the code

6.3 Ref

  • A reference to a DOM element can be obtained through ref
  • Refs can also take the form of callback functions
  • A REF can be exposed to a parent component through forwarding
# Basic usagelet myDiv;
<div ref={myDiv}>My Element</div>; <div ref={el= > / * handle el... * /}>My Element</div> # forward <canvas ref={props"256" height="256" /> / / child component
  
<Canvas ref={canvas}/ / the parent componentCopy the code

6.4 instruction

  • throughuse:Namespaces support custom directives. But this is justrefA useful syntactic sugar that is similar to native bindings and can have multiple bindings on the same element without conflict. This allows us to take better advantage of reusable DOM element behavior
  • The custom instruction is only of the form(element, valueAccesor)WherevalueAccessorIs a function that gets the binding value. As long as the function is imported in scope, you can passuse:Use it
# click-outsie.js
export default function clickOutside(el, accessor) {
  // implement here
  const onClick = (e) = >! el.contains(e.target) && accessor()? . ()document.body.addEventListener("click", onClick)
  onCleanup(() = > document.body.removeEventListener("click", onClick))
}

# indeex.js
import { render } from "solid-js/web"
import { createSignal, Show } from "solid-js"
import clickOutside from "./click-outside"

function App() {
  const [show, setShow] = createSignal(false)

  return (
    <Show
      when={show()}
      fallback={<button onClick={(e)= > setShow(true)}>Open Modal</button>}
    >
      <div class="modal" use:clickOutside={()= > setShow(false)}>
        Some Modal
      </div>
    </Show>
  );
}

render(() = > <App />.document.getElementById("app"))


Copy the code

7.Props

  • The Props object is read-only and has reactive properties encapsulated as getters for the object
  • mergeProps: Merge of reactive objectsmergeMethods. Use to set default props for the component in case the caller does not provide these property values, or clone props objects that contain reactive properties
  • splitProps: splitPropsIt’s an alternative to deconstruction.splitPropsSplit reactive objects by keys while remaining reactive, receiving a props object and an array of keys for props. Returns an array whose first element is the object corresponding to the array of input keys. The last element in the array will be an props object with an unspecified key name, similar to the remaining parameters
// Set the default props
props = mergeProps({ name: "Smith" }, props);

/ / clone props
newProps = mergeProps(props);

/ / merge props
props = mergeProps(props, otherProps);

/ / separation propos
const [local, others] = splitProps(props, ["children"]); <> <Child {... others} /> <div>{local.children}<div> </>Copy the code
# parent component <Greeting Greeting ="Hello"/> # child componentconst merged = mergeProps({ greeting: "Hi".name: "John" }, props);
return <h3>{merged.greeting} {merged.name}</h3>
Copy the code
# extension, similar to React props <Info {... pkg} />Copy the code
Children: accepts the JSX element inside the component tagfunction ColoredList(props) {
  return <>{props.children}</>} # If we had a dynamic list, we would interact directly with props. Children and not only create the node multiple times # children (Api) : this is a utility function that addresses the responsiveness of hierarchy nesting and returns a Memo. Except in cases where values are passed directly to JSX, the props. Children method is recommendedconst list = children(() = > props.children);
// What can I do with list
createEffect(() = > list())

Copy the code

8.Store State storage

8.1 create the Store

  • Store creates a Signal tree as a proxy through createStore, allowing individual values in nested data structures to be tracked independently. This is often used with nested data structures such as arrays of objects

  • The Store proxy object only tracks accessed properties. And recursively generates nested Store objects on nested data when accessing stores, but it only wraps arrays and ordinary objects, not classes, so things like Date, HTMLElement, RegExp, Map, Set, and so on are not responsive granularity. In addition, top-level state objects cannot be traced without accessing properties on them

  • A top-level state object cannot be traced without accessing properties on the object. Therefore, it does not apply to iterated objects because adding a new key or index does not trigger an update. Therefore, instead of trying to use the state object itself, place the array on the key

const [state, setState] = createStore(initialValue)

/ / read the values
state.someValue

/ / set the value
setState({ merge: "thisValue" })

setState("path"."to"."value", newValue)

// Use the list as the key of the status object
const [state, setState] = createStore({ list: []})// Access the 'list' property on the state object
<For each={state.list}>{item= > / *... * /}</For>
Copy the code

8.2 the Getter

  • The Store object supports the use of getters to Store computed values
# 1.Direct use ofconst [state, setState] = createStore({
  user: {
    firstName: "John".lastName: "Smith".get fullName() {
      return `The ${this.firstName} The ${this.lastName}`; }},})Copy the code
# 2.Use the memolet fullName
const [state, setState] = createStore({
  user: {
    firstName: "John".lastName: "Smith".get fullName() {
      return fullName();
    },
  },
})
fullName = createMemo(() = > `${state.user.firstName} ${state.user.lastName}`)
Copy the code

8.3 update the Store

  • Changing state can take the form of a function that passes the previous state and returns the new state or value. Objects are always shallow merged. Set the value toundefinedTo remove the property from the Store
const [state, setState] = createStore({
  firstName: "John".lastName: "Miller"}); setState({firstName: "Johnny".middleName: "Lee" });
// ({ firstName: 'Johnny', middleName: 'Lee', lastName: 'Miller' })

setState((state) = > ({ preferredName: state.firstName, lastName: "Milner" }));
// ({ firstName: 'Johnny', preferredName: 'Johnny', middleName: 'Lee', lastName: 'Milner' })

Copy the code
  • The paths supported by setState include key arrays, object scopes, and filter functions.
  • SetState also supports nested Settings where you can specify the path to change. In the case of nesting, the state to be updated may be a non-object value. Objects are still merged, but other values (including arrays) will be replaced.

const [state, setState] = createStore({
  counter: 2.list: [{id: 23.title: 'Birds' }
    { id: 27.title: 'Fish'}}]); setState('counter'.c= > c + 1)
setState('list'.l= > [...l, {id: 43.title: 'Marsupials'}])
setState('list'.2.'read'.true)

/ / {
// counter: 3,
// list: [
// { id: 23, title: 'Birds' }
// { id: 27, title: 'Fish' }
// { id: 43, title: 'Marsupials', read: true }
/ /]
// }
Copy the code
  • Paths can be string keys, key arrays, iterated objects ({from, to, by}), or filter functions

const [state, setState] = createStore({
  todos: [{task: 'Finish work'.completed: false }
    { task: 'Go grocery shopping'.completed: false }
    { task: 'Make dinner'.completed: false}}]); setState('todos'[0.2].'completed'.true);
/ / {
// todos: [
// { task: 'Finish work', completed: true }
// { task: 'Go grocery shopping', completed: false }
// { task: 'Make dinner', completed: true }
/ /]
// }

setState('todos', { from: 0.to: 1 }, 'completed'.c= >! c);/ / {
// todos: [
// { task: 'Finish work', completed: false }
// { task: 'Go grocery shopping', completed: true }
// { task: 'Make dinner', completed: true }
/ /]
// }

setState('todos'.todo= > todo.completed, 'task'.t= > t + '! ')
/ / {
// todos: [
// { task: 'Finish work', completed: false }
// { task: 'Go grocery shopping! ', completed: true }
// { task: 'Make dinner! ', completed: true }
/ /]
// }

setState('todos', {}, todo= > ({ marked: true.completed: !todo.completed }))
/ / {
// todos: [
// { task: 'Finish work', completed: true, marked: true }
// { task: 'Go grocery shopping! ', completed: false, marked: true }
// { task: 'Make dinner! ', completed: false, marked: true }
/ /]
// }

Copy the code
  • Immer inspired Solid’s Store objectproduceAPI, which allows local modification of state
setState(
  produce((s) = > {
    s.user.name = "Frank";
    s.list.push("Pencil Crayon"); }));Copy the code
  • reconcileUsed to process immutable data from store or giant API responses
// Subscribe to an Observable
const unsubscribe = store.subscribe(({ todos }) = > (
  setState('todos', reconcile(todos))); ) ; onCleanup(() = > unsubscribe());
Copy the code
  • createMutableCreate a new mutable Store proxy object. Store only triggers updates when a value changes. Tracing is done by intercepting property access and automatically tracking deeply nested data through the proxy.
  • createMutableIt can be useful for integrating external systems or as a compatibility layer with MobX/Vue
const state = createMutable(initialValue);

/ / read the values
state.someValue;

/ / set the value
state.someValue = 5;

state.list.push(anotherValue);
Copy the code

9. Context

9.1 Using Context Context

  • Context provides a form of dependency injection. It can be used to avoid situations where data needs to be passed as props through an intermediate component
  • This function creates a new context object that can be passeduseContextTo use and provideProviderControl flow. When not found at the top of the hierarchyProvider, the default context is used
# Store

import { createSignal, createContext, useContext } from 'solid-js'

const CounterContext = createContext()

export function CounterProvider(props) {
  console.log(props)
  const [count, setCount] = createSignal(props.count || 0)
  const store = [
    count,
    {
      increment() {
        setCount(c= > c + 1)},decrement() {
        setCount(c= > c - 1)}}]return (
    <CounterContext.Provider value={store}>{props.children}</CounterContext.Provider>)}export function useCounter() {
  return useContext(CounterContext)
}

Copy the code
# Nested.js

import { useCounter } from './store/counter'

export default function Nested() {
  const [count, { increment, decrement }] = useCounter()
  return (
    <>
      <div>{count()}</div>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </>)}Copy the code
# test.js

import { useCounter } from './store/counter'

export default function Test() {
  const [count, { increment, decrement }] = useCounter()

  return (
    <>
      <div>{count()}</div>
      <button onClick={increment}>add</button>
      <button onClick={decrement}>sub</button>
    </>)}Copy the code
# index.js

import { render } from 'solid-js/web'
import { CounterProvider } from './store/counter'
import Nested from './nest'
import Test from './text'

function App() {
  return <>
    <h1>Welcome to Counter App</h1>
    <Nested></Nested>
    <Test></Test>
  </>
}

render(() = > (
  <CounterProvider count={100}>
    <App/>
  </CounterProvider>
), document.getElementById('root'))

Copy the code

9.2 Not Using Context


import { createSignal } from 'solid-js';

export default createSignal(0);

// Code somewhere else
import counter from './counter';
const [count, setCount] = counter;
Copy the code

10. The response type

10.1 Batch Update

  • The SolidbatchUtility functions allow multiple changes to be pushed into the queue and then used simultaneously before notifying the observer. Signal values updated in a batch are not committed until the batch is complete
import { render } from "solid-js/web"
import { createSignal, batch } from "solid-js"

const App = () = > {
  const [firstName, setFirstName] = createSignal("John");
  const [lastName, setLastName] = createSignal("Smith");
  const fullName = () = > {
    console.log("Running FullName")
    return `${firstName()} ${lastName()}`
  } 
  const updateNames = () = > {
    console.log("Button Clicked")
    batch(() = > {
      setFirstName(firstName() + "n")
      setLastName(lastName() + "!")})}return <button onClick={updateNames}>My name is {fullName()}</button>
};

render(App, document.getElementById("app"))


Copy the code

10.2 Untrack

  • Sometimes you want Signal read behavior not to be tracked, even in a reactive context, which Solid providesuntrackUtility functions to prevent the wrapper calculation from tracking any read behavior
  • Let’s say we don’t want tobLogs are generated when changes are made. We can cancel the trace by changing Effect to the followingb Signal
import { render } from "solid-js/web";
import { createSignal, createEffect, untrack } from "solid-js";

const App = () = > {
  const [a, setA] = createSignal(1);
  const [b, setB] = createSignal(1);

  createEffect(() = > {
    console.log(a(), untrack(b));
  });

  return <>
    <button onClick={()= > setA(a() + 1)}>Increment A</button>
    <button onClick={()= > setB(b() + 1)}>Increment B</button>
  </>
};

render(App, document.getElementById("app"));

Copy the code

10.3 On

  • Solid provides aonUtility functions that set explicit dependencies for our calculations. This is mainly used to more explicitly and succinctly declare which signals to track. However, it also allows calculations to run not immediately but only on the first change. You can usedeferOption to enable this feature
  • CreateEffect by default tracks the reactive data used to be read, and on allows you to specify which object to track

import { render } from "solid-js/web"
import { createSignal, createEffect, on } from "solid-js"

const App = () = > {
  const [a, setA] = createSignal(1)
  const [b, setB] = createSignal(1)

  createEffect(on(a, (a) = > {
    console.log(a, b())
  }, { defer: true }))
  
  return <>
    <button onClick={()= > setA(a() + 1)}>Increment A</button>
    <button onClick={()= > setB(b() + 1)}>Increment B</button>
  </>
}

render(App, document.getElementById("app"))

Copy the code

11. The asynchronous

11.1 Lazy Loading of Components

  • The SolidlazyMethod allows dynamic imports of wrapped components to implement lazy loading. It then outputs a component that can be used normally in a JSX template that dynamically loads the underlying imported code internally on the first render, at which point it pauses the render branch until the code is available
# General introductionimport Greeting from "./greeting"# lazy to introduceconst Greeting = lazy(() = > import("./greeting"))

Copy the code
# Delay simulationimport { render } from "solid-js/web";
import { lazy } from "solid-js";

const Greeting = lazy(async() = > {// Simulate delay
  await new Promise((r) = > setTimeout(r, 1000));
  return import("./greeting");
})

function App() {
  return (
    <>
      <h1>Welcome</h1>
      <Greeting name="Jake" />
    </>
  );
}

render(() = > <App />.document.getElementById("app"));
Copy the code

11.2 resources

  • Resource is a special Signal specifically designed to handle asynchronous loading
  • The generated Resource Signal also contains reactiveloadingerrorProperty to easily control our view based on the current state
  • The second value returned by createResource contains onemutateMethod to update the internal Signal directly, and one morerefetchMethod that can be used to reload the current query request even if the source has not changed
import { createSignal, createResource } from "solid-js"
import { render } from "solid-js/web"

const fetchUser = async (id) =>
  (await fetch(`https://swapi.dev/api/people/${id}/ `)).json();

const App = () = > {
  const [userId, setUserId] = createSignal()
  const [user, { mutate, refetch }] = 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>
    </>
  )
}

render(App, document.getElementById("app"))
Copy the code

11.2 Suspense

  • Solid also provides a mechanism to coordinate the display of multiple asynchronous events. Suspense acts as a boundary to show fallback placeholders rather than partially loaded content when these asynchronous events are not complete
  • The triggerSuspenseIs the reading of asynchronously derived values. Not the asynchronous fetch behavior itself. If theSuspenseThe resource Signal is not read under the boundary (includinglazyComponents),SuspenseWill not hang
# index.js
import { render } from "solid-js/web"
import { lazy, Suspense } from "solid-js"

const Greeting = lazy(async() = > {// simulate delay
  await new Promise(r= > setTimeout(r, 1000))
  return import("./greeting")})function App() {
  return (
    <>
      <h1>Welcome</h1>
      <Suspense fallback={<p>Loading...</p>} ><Greeting name="Jake" />
      </Suspense>
    </>
  )
}

render(() = > <App />.document.getElementById("app"))

# greeting.js
export default function Greeting(props) {
  return <h3>Hi, {props.name}</h3>
}


Copy the code

Suspense List (Experiments)

  • SuspenseListCan coordinate multiple parallelSuspenseSuspenseListComponents. It controls the order in which content is displayed to reduce layout jitter, and has options to control folding or hiding fallback states
<SuspenseList revealOrder="forwards" tail="collapsed">
  <ProfileDetails user={resource.user} />
  <Suspense fallback={<h2>Loading posts...</h2>} ><ProfileTimeline posts={resource.posts} />
  </Suspense>
  <Suspense fallback={<h2>Loading fun facts...</h2>} ><ProfileTrivia trivia={resource.trivia} />
  </Suspense>
</SuspenseList>
Copy the code

11.4 the Transition

  • Used for batch asynchronous updates in deferred commit transactions after all asynchronous processing has completed. This is associated with Suspense and only tracks resources read under Suspense boundaries
const [isPending, start] = useTransition();

// Check if it is in transition
isPending();

// Wrapped in transition
start(() = > setSignal(newValue), () = > /* Transition completed */)
Copy the code