A component is a function takes some props and renders a view. In these props, you may have some state elements. When you do multi-page SPAs, you rebuild components on every new page. You may want to use a global store to share state between them. A useful pattern for this is is the Context. You can also do this by parametrizing the function component:
(context) => function Component(props) {HTML like stuff}
Below is a simple example of a navigation between pages that renders data using the context pattern and SolidJS:
You can pass in all the stuff you may need along the road such as themes, useful functions...
With some frameworks, you can declare state outside of components. With SolidJS
, you use createSignal
to declare a state element; it can be encapsulated in a component, or declared outside of it. You can take advantage of this "context": you can declare it at the context level if you need to share it among components.
// context.js
import {createSignal} from from "solid-js";
const [bool, setBool] = createSignal(false);
const context = {
bool, setBool,
theme: {...},
...
}
In the example below, we get the state from the context, with the getter and setter.
const comp1 = (ctx) => {
const {bool, setBool, theme} = ctx;
return const Comp1 = (props) =>
(
<p>The state value is: {bool() ? "true" : "false"}</p>
<button onClick={()=> setBool(v => !v}>Toggle</button>
{props?.children}
)
}
[...]
import context from "./context";
const Comp1 = comp1(context)
<Comp1>Hi</Comp1>
When you navigate to another component, you import the state and have the reactive value every time you navigate back to this component.
const comp2 = (ctx) => {
const {bool} = ctx;
return () =>
(
<div>
<p>Comp1 changed the state: {bool()}</p>
</div>
)
}
[...]
import context from "./context";
const Comp2 = comp2(context)
<Comp2/>
We can have an async function that sets data from a component, and we want to use the result elsewhere. When you want to run an async call with SolidJS, it is specified in the SolidJS documentation that you should use createResource
. Then, if you need ot update the state, you should avoid createEffect
as this is used to mutate the DOM but rather attach a then(..)
to the async call. Let's do that.
We mock an async call and update the state with the result. Furthermore, since we may want to write this function out of the scope of the component, we parametrize it with the context so the state setter will be available:
// asyncFunc.js
const asyncFunc = ({setData}) =>
(x) =>
new Promise(resolve =>
setTimeout(() => resolve(x), 1000)
)
.then(setData)
We first add a signal in the context to share the data within components:
// context.js
import {createSignal} from from "solid-js";
const [data, setData] = createSignal("");
const context = {
data, setData,
...
}
The async calls are handled with createRessource
in SolidJS: it can take an argument and returns a getter function (written in the form (argOfFun, Fun)
).
import { createEffect, createResource } from "solid-js";
import context from "./context";
import asyncFunc from "./asyncFunc";
const comp3 = (ctx) => {
// import the state
const {data, setData, theme} = ctx;
const AsyncFun = asyncFunction(ctx)
return function Comp1(props) {
// run the async function; it will update the state
const [result] = createResource(100, AsyncFun);
return(
<p> Async render: {result()}</p>
)
}
}
You can use it elsewhere by importing the state data
as above and when you navigate, the component will be up-to-date.
Note that the function component will only run once, so the async function will be run only once.
What if we want to change it? Lets give an example. We use a slider to change the parameter of the async function.
We use a local state to track the value just for the display; this means it will be reset every time the component is rendered. You can choose a global state if you want persistence.
We need to implement 2 async calls: the first one will run when the component is rendered, and the second will react on changes during the life time of the component. SolidJS makes this easy as we are assured the function component is run only once.
import ...
const comp4 = (ctx) => {
// import the state from context
const {data, setData} = ctx;
const AsyncFun = asyncFunction(ctx)
return ()=> {
// local state to display the slider values, reset on every mount
const [slide, setSlide] = createSignal(10);
// initial async call on navigation
!data() && createResource(slide(), AsyncFun);
return (
<div>
<input
type="range"
min="10"
max="100"
value="10"
onchange={({target: {value}) => {
setSlide(value);
// dynamic async call
createResource(value, AsyncFun)
}}
/>
<p>{slide()}</p>
<p> Dynamic async update: {data()}.</p>
</div>
)
Any other component can access to the reactive state data
.
Top comments (0)