Releases: solidjs/solid
v0.26.0 - Call You, Maybe
This release is about finalizing some API changes on the road to 1.0. This one has one breaking change and not much else.
Signals no longer always notify by default
Solid's original behavior has been to always notify on signal change even if the value hasn't changed. The idea was to simulate stream behavior. However, this has some downsides:
- Inconsistent with State.. I made the decision to make state equality check by default, it is weird signals and memo's do not.
- More likely to hit infinite loops. Equality check naturally stops infinite loops in some cases. While infinite loops aren't good and code that produces them suspect, it is nice to keep things clean.
- It is consistent with other modern reactive libraries like MobX and Vue.
The API has not changed. You can opt out of the default behavior by passing in your own comparator or false to the 2nd parameter of createSignal
and the 3rd parameter of createMemo
.
My hope this is the last release before I start making 1.0 RC's. This one has big enough impact I want to get this out first. I imagine the remaining changes will be just syntax.
v0.25.0 - Master -> Main
This release is about refining the the APIs as we approach the our release candidate for 1.0. And moving from master
to main
.
Breaking Changes
Resource API
Minor difference to allow the first argument to be optional and support more features in the future. New full signature is:
export function createResource<T, U>(
fn: U | false | (() => U | false),
fetcher: (k: U, getPrev: () => T | undefined) => T | Promise<T>,
options?: { initialValue?: T }
): ResourceReturn<T>;
3rd argument is now an options object instead of just the initial value. This breaking. But this also allows the first argument to be optional for the non-tracking case. Need a promise that only loads once? Don't have need to re-use the fetcher. Do this:
const [data] = createResource(
async () => (await fetch(`https://someapi.com/info`)).json()
);
on/onCapture
These are an escape hatch for unusual events. Previously these were custom attributes but now they are namespaced like:
<div on:someUnusualEvent={e => console.log(e.target)} />
change main
field to be node
Now that we are supporting SSR for legacy(non-ESM) systems I need to use the main field to indicate a node env. We will be using the "browser" field for the client build in Solid. This straight up breaks Jest which doesn't respect that. I've created solid-jest
to handle this.
https://github.com/ryansolid/solid-jest
New Features
Namespace Types
Types added for Namespace attributes. You probably won't need most of these because they are for more advanced usage. However to use them you need to extend the JSX Namespace:
declare module "solid-js" {
namespace JSX {
interface Actions { // use:____
}
interface ExplicitProperties { // prop:____
}
interface ExplicitAttributes { // attr:____
}
interface CustomEvents { // on:____
}
interface CustomCaptureEvents { // oncapture:____
}
}
}
Lazy component preload
Lazy components now have a preload function so you can pre-emptively load them.
const LazyComp = lazy(() => import("./some-comp"))
// load ahead of time
LazyComp.preload();
Error Boundary reset
Error boundaries now have the ability to reset themselves and try again. It is the second argument to the fallback.
<ErrorBoundary fallback={(err, reset) => {
if (count++ < 3) return reset();
return "Failure";
}}><Component /></ErrorBoundary>
v0.24.0 - Unified SSR
This release is the start of the rework of the SSR solution. Consolidating them under a single method. Unfortunately, this one comes with several breaking changes.
Breaking Changes
Removed solid-js/dom
It's been a few versions deprecated. It's gone.
Updated Resource API
Changed to more resemble SWR and React Query. Needed to remove createResourceState
so now need to use a getter over createResource
to get same effect. See updated documentation.
Change SSR render call signatures
They now return results objects that include the generated hydration script. No more need to generate it separately. Also comes autowrapped in the script
tag now.
assignProps
to mergeProps
While you use them the same way mostly it no longer has Object.assign
semantics and always returns a new object. This is important as in many cases we need to upgrade to a Proxy.
Renamed getContextOwner
to getOwner
Removes confusion around context and consistent with new helper runWithOwner
.
Solid Element no longer uses State for props
This reduces the size of the library especially for those not using state. It also should slightly increase performance as no need for deep nesting of proxies. It also makes things behave more consistently avoided unintended deep wrapping.
Non-breaking Changes
New non-reactive Async SSR
I have now combined sync/streaming/async SSR into the same compiler output. To do so I have developed a new non-reactive Async SSR approach. After realizing how fast Solid renders, it occurred to me on the server we could do a much simpler approach if we were willing to re-render all content in Suspense boundaries. While that is some wasted work, compared to including the reactive system it's a killing.
Increase SSR Performance
Through reusing static strings in the template we reduce repeated creation costs. This small improvement can make 5-8% improvements where you have many rows.
Event Delegation
Solid is now being more strict on what events it delegates. Limiting to standard pointer/touch/mouse/keyboard events. Custom events will no longer be delegated automatically. This increases compatibility for Web Component users who don't compose their events. Non-delegated events will still work and binding array syntax with them.
State getters no longer memos
Automatic memos put some constraints on the disposal system that get in the way of making the approach flexible to hold all manner of reactive primitives. Some previous limitations included not being able to have nested getters. You can still manually create a memo and put it in a getter but the default will not be memoized.
New Features
children
helper
Resolves children and returns a memo. This makes it much easier to deal with children. Using same mechanism <Switch>
can now have dynamic children like <For>
inside.
"solid" Export Conidition
This is the way to package the JSX components to be compiled to work on server or client. By putting the "solid" condition the source JSX will be prioritized over normal browser builds.
Bug Fixes
- Top level primitive values not working with
reconcile
- Fix Dynamic Components to handle SVG
- Rename potentially conflicting properties for event delegtion
- Fixed State spreads to not loose reactiviy. Added support for dynamically created properties to track in spreads and helpers
- TypeScript, always TypeScript
v0.23.0
This release is mostly to support breaking change for TS users. JSX types no longer pollutes the global namespace. This means you need to update your projects to import it.
For users TS 4.1 or above add to your tsconfig to have JSX types in all your TSX files:
"compilerOptions" {
"jsx": "preserve",
"jsxImportSource": "solid-js",
}
Or mixing and matching? You can set JSX types per file using the pragma at the top of each file:
/* @jsxImportSource solid-js */
You can now import JSX
types directly from Solid as neccessary:
import { JSX } from "solid-js";
For instance, to add a custom element you would:
import { JSX } from "solid-js";
declare module "solid-js" {
export namespace JSX {
interface IntrinsicElements {
foo: CustomFooHTMLElementAttributes
}
}
}
v0.22.0
Unified Exports (Deprecation solid-js/dom
)
Solid now has streamlined exports for isomorphic development. This means from now on using solid-js/web
instead of solid-js/dom
. Based on compiler options it will swap out the appropriate packages for web. You should only ever import solid-js
, solid-js/h
, solid-js/html
, and solid-js/web
directly in your code.
solid-js/web
now exports an isServer
field which indicates whether the code is executed for server rendering. This is constant in the respective packages meaning it can allow for powerful treeshaking/dead code elimination in final bundles even when used directly in end user code or 3rd party libraries.
Dev Mode
Aliasing solid-js
to solid-js/dev
links in a Dev mode for Solid. It's still a WIP process but it introduces some new APIs. First signals and state (and resources) have the ability to set a name for debug purposes as an options argument.
We also export a serializeGraph
method which will serialize all the signals below the executing context in the reactive graph.
Finally there is a new globalThis._$afterUpdate
hook that can be assigned that will be called after every render that can be used for tracking purposes.
This is just the start but it is my intention to develop these features to allow for better HMR and DevTools.
Note: If the libraries are not being pulled into your bundle and are treated as external you may need to alias
solid-js
tosolid-js/dev
in your bundler in order to use dev mode.
Self contained HyperScript/Lit Modules
We now ship the respective DOM expressions code. This makes it much easier to use directly from a CDN like Skypack. You literally can develop with Solid in the old school write it in notepad before npm was a thing sort of way.
<html>
<body>
<script type="module">
import { createSignal, onCleanup } from "https://cdn.skypack.dev/solid-js";
import { render } from "https://cdn.skypack.dev/solid-js/web";
import html from "https://cdn.skypack.dev/solid-js/html";
const App = () => {
const [count, setCount] = createSignal(0),
timer = setInterval(() => setCount(count() + 1), 1000);
onCleanup(() => clearInterval(timer));
return html`<div>${count}</div>`;
};
render(App, document.body);
</script>
</body>
</html>
Save this in a text file called "site.html" and double click it and instant Solid in your browser.
renderToWebStream
New renderToWebStream
for synchronous SSR mode. This allows us to stream from things like Cloudflare Workers.
createMutable
New mutable state primitive. Useful for interopt with other libraries. We can use this potentially for things like Vue/MobX compat. Or when we need to interact with libraries that can't be aware of Solid's reactive system, yet we want to capture updates. It supports getters and setters.
Use with caution as it can promote difficult to reason about code, anti-patterns, and unexpected performance cliffs. Keep in mind Vue and MobX care less about these inefficient patterns since they have a VDOM safety net. We do not. For advanced users only.
const user = createMutable({
firstName: "John",
lastName: "Smith",
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
set fullName(value) {
const parts = value.split(" ");
batch(() => {
this.firstName = parts[0];
this.lastName = parts[1];
});
}
});
console.log(user.fullName); // John Smith
user.fullName = "Jake Murray";
console.log(user.firstName); // Jake
State Getter/Setters are now Wrapped
Getters are now wrapped in createMemo
and setters in batch
. However, this introduces a new limitation that they can only be top level to have this behavior.
State compatible with Prop Helpers
You can now use state with assignProps
and splitProps
helpers.
Removed DOM SSR
No longer supporting hydratable DOM SSR in patched(ie... JSDOM) node environments. Use the standard SSR methods instead. Can still run Solid in JSDOM for things like Jest, but can't be used for isomorphic development.
v0.21.0
Attribute and Prop changes
We will now default to using Attributes where possible to be consistent. Solid is aiming to generally reflect the case insensitiveness of HTML. Custom Elements remain the one place that defaults to property setters on Dynamic elements.
While TypeScript 4.2 is yet to be released, we are introduce attr
, prop
, use
and style
namespace directives. To allow more expressiveness in binding syntax.
Other Changes
- New
on
andonMount
helpers - More performant SSR escaping
- Lazy eval SSR Component props (fix SSR Context API)
- Add support for SSR with Solid Styled Components
- Fix Lit Dom Expressions style in Template tags
- Fix JSX Types
V0.20.0
Re-scheduling Reactivity.
This release makes large changes to the Reactive System. Key changes are deferring createEffect
to be after rendering and introducing createComputed
do immediate reactive graph updates like loading async data.
Concurrency
In addition, the reactive model brings updates to Suspense and Transitions. Solid now has true concurrent rendering at a granular level. This mechanism does differ from React as it currently only supports a single future.
createSelector
New API to do delegated selection. Good for updating only the rows that change in large lists:
const [selected, setSelected] = createSignal();
const isSelected = createSelector(selected);
return <For each={list()}>{
item => <li class={isSelected(item.id) ? "selected" : ""}>{item.text}</li>
}</For>
This will only re-evaluate the class binding when the value equals the id passed in or if the previous value was that id. So 100 rows and only 2 executions (remove old, and set new). Optionally takes a comparison function so you can do things like multi-select as well.
as
prop in Styled Components
Let's you override the element type in the view where used.
Removed APIs
afterEffects
, createDependentEffect
, and suspend
have been removed as they no longer make sense with the new reactive system timing.
v0.19.0
API Changes to support better SSR
Breaking Changes:
Set State
Mutable form is no longer a default. It was strangely inconsistent as you could accidentally mutate in immutable forms. No indicator why it should behave differently and work. Increased the size of state
for everyone and added performance overhead with additional proxy wrapping. Also it was based on returning undefined meaning function forms could never return undefined to blank a vlue. Solid has changed it into a state setter modifier produce
after ImmerJS naming.
// top level
setState(produce(s => {
s.name = "John"
}));
// nested
setState('user', produce(s => {
s.name = "John"
}));
Prop APIs
After writing setDefaults
, cloneProps
, and about to introduce mergeProps
it became clear we can do this all with a single assignProps
helper. So the former has been removed and now we have:
// default props
props = assignProps({}, { name: "Smith" }, props);
// clone props
newProps = assignProps({}, props);
// merge props
assignProps(props, otherProps)
It follows the same pattern as ES Object.assign
adding properties to the first argument and returning it. Except this method copies property descriptors without accessing them to preserve reactivity.
freeze
& sample
have been renamed
These APIs never had the most obvious naming, borrowing from SRP and digital circuit concepts rather than common english. They are now batch
and untrack
respectively which better reflect their purpose. These are now deprecated and will be removed in next minor version.
Resource API
For better automatic hydration support it is prudent to change resource signatures to take functions that return promises rather than promises themselves. This factory function has a lot advantages. This allows the library to decide whether to execute it or not. In certain cases we can choose skipping creating the promise altogether. It also leaves the door open for things like retry.
We use this mechanism to wire up streamed data from the server and automatic data hydration for resources rendered into the page in async SSR.
SSR Improvements
New experimental support for Suspense aware synchronous, asynchronous, and streaming SSR with hydration, progressive hydration, and automatic isomorphic data serialization. Completely removed what was there before with a simple static generator and more examples, so all existing projects using solid-ssr
package will break with this release. This is a much better foundation, and I hope to build better things on top.
New
State Getters
For convenience of passing derived values or external reactive expressions through Solid's state initializer you can now add getter
's.
const [state, setState] = createState({
firstName: "Jon",
lastName: "Snow",
get greeting() { return `You know nothing ${state.firstName} ${state.lastName}` }
});
return <div>{state.greeting}</div>
Control Flow
Dynamic allows swapping Component dynamically.
// element tag name
const [comp, setComp] = createSignal("h1");
<Dynamic component={comp()} {...otherProps} />
// Component
setComp(MyComp);
ErrorBoundary catches uncaught downstream errors and shows a fallback.
<ErrorBoundary fallback={<div>Something went terribly wrong</div>}>
<MyComp />
</ErrorBoundary>
Portals render in the Head
You can now render portals in the head with no additional div element.
Multi-version detection
Common hard to track issue with Solid is when multiple versions of the library are running on the same page. It breaks reactivity, and is sometimes difficult to notice. Solid now detects if a version has already been loaded at runtime and complains.
Bug Fixes & Updates
Arguably a new feature but Solid now detects computation owners with pending dependency changes when trying to resolve nested computations. In so it will resolve those dependencies first. This fixes a long time issue with conditional processing with not directly related reactive atoms.
Improved TypeScript Types.
v0.18.0
A lot of bug fixes, and introduction of string based SSR.
Breaking Changes:
- Removal of
forwardRef
. Value and function handled by justref
. - Change to how TypeScript is managed. Brought all JSX types inside the repo, and improved Component typing.
- Changed default renderer in
solid-ssr
to string renderer. - Change inline styles to use
setProperty
and hyphenated or camelCase.
Lots of small bug fixes.
v0.17.0
Consolidation in preparation for release candidate
- Big refactor of core reactive system and render list reconciler
- Significantly smaller reducing core by atleast 3kb minified
- Better handling of nested reactive nodes in Fragments
- Update SSR mechanisms, added progressive event hydration, created repo for SSR environment (
solid-ssr
) @once
compiler hint to statically bind values- Better wrapping hueristics for booleans and ternaries in JSX
Breaking Changes
- Removed
transform
prop from control flow. Idiomatic approach is to make a HOC for transformations of this nature. - Removed selectWhen/selectEach control flow transforms.
- Changed event system
on____
prop to stop differentiating on case. Super confusing.Instead will try to delegate unless unable. Made TypeScript all CamelCase (although technically both forms behave identically)- Removed
model
event delegation approach. Instead to create bound event use array:onClick={[handler, row.id]}
. Inspired by Inferno'slinkEvent
helper. - Renamed
events
prop toon
prop - Added
onCapture
prop for capture events