Skip to content

Commit c276ab6

Browse files
authored
Merge pull request #152 from ryansolid/next
Update DOM Expressions, TypeScript, and lots of bug fixes
2 parents aad3fb2 + 5be6a18 commit c276ab6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+3674
-1946
lines changed

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
2-
## 0.17.0 - 2020-03-??
2+
## 0.18.0 - 2020-05-01
3+
A lot of bug fixes, and introduction of string based SSR.
4+
Breaking Changes:
5+
* Removal of `forwardRef`. Value and function handled by just `ref`.
6+
* Change to how TypeScript is managed. Brought all JSX types inside the repo, and improved Component typing.
7+
* Changed default renderer in `solid-ssr` to string renderer.
8+
9+
## 0.17.0 - 2020-03-24
310
A lot of consolidation in preparation for release candidate
411
* Big refactor of core reactive system and render list reconciler
512
* Significantly smaller reducing core by atleast 3kb minified

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,10 @@ And check out the Documentation, Examples, and Articles below to get more famili
162162
## Documentation
163163

164164
- [State](https://github.com/ryansolid/solid/blob/master/documentation/state.md)
165-
- [Signals](https://github.com/ryansolid/solid/blob/master/documentation/signals.md)
165+
- [Reactivity](https://github.com/ryansolid/solid/blob/master/documentation/reactivity.md)
166166
- [JSX Rendering](https://github.com/ryansolid/solid/blob/master/documentation/rendering.md)
167167
- [Components](https://github.com/ryansolid/solid/blob/master/documentation/components.md)
168+
- [Styling](https://github.com/ryansolid/solid/blob/master/documentation/styling.md)
168169
- [Context](https://github.com/ryansolid/solid/blob/master/documentation/context.md)
169170
- [Suspense](https://github.com/ryansolid/solid/blob/master/documentation/suspense.md)
170171
- [API](https://github.com/ryansolid/solid/blob/master/documentation/api.md)

documentation/api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Creates a new State object and setState pair that can be used to maintain your c
66

77
### `createSignal(initialValue, comparatorFn): [getValueFn, setValueFn]`
88

9-
This is the smallest and most primitive reactive atom used to track a singl value. By default signals always notify on setting a value. However a comparator can be passed in to indicate whether the values should be considered equal and listeners not notified.
9+
This is the smallest and most primitive reactive atom used to track a single value. By default signals always notify on setting a value. However a comparator can be passed in to indicate whether the values should be considered equal and listeners not notified.
1010

1111
### `createEffect(prev => <code>, initialValue): void`
1212

documentation/comparison.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This library owes its existence to Knockout. Modernizing its model for fine grai
88

99
Knockout's bindings are just strings in HTML which are walked over at runtime. They depend on cloning context($parent etc...). Whereas Solid uses JSX or Tagged Template Literals for templating opting for an in JavaScript API.
1010

11-
The biggest difference might be that Solid uses SRP which ensures synchronicity of changes whereas Knockout has deferUpdates which uses a deferred microtask queue.
11+
The biggest difference might be that Solid's approach to batching changes which ensures synchronicity whereas Knockout has deferUpdates which uses a deferred microtask queue.
1212

1313
## React
1414

@@ -20,7 +20,7 @@ However, as much as Solid aligns with React's design philosophy, it works signif
2020

2121
Svelte pioneered the precompiled disappearing framework that Solid also employs to a certain degree. Both libraries are truly reactive and can produce really small execution code bundles although Svelte is the winner here for small demos. Solid requires a bit more explicitness in its declarations and relying less on implicit analysis from the compiler, but that is part of what gives Solid superior performance. Solid also keeps more in the runtime which scales better in larger apps. Solid's RealWorld demo implementation is 25% smaller than Svelte's.
2222

23-
Both libraries aim to help their developers write less code but approach it completely differently. Svelte 3 focuses on the optimization of the ease of dealing with localized change focusing on plain object interaction and 2 way binding. In constrast Solid deliberately embraces CQRS and immutable interface. Solid uses proxies to track dependencies but very consciously blocks setters, considering 2 way binding and direct mutation a dangerous anti-pattern in terms of large scale solutions. Instead Solid has adopted expressive setters(influenced by ImmutableJS and Falcor) to give flexibility of plain objects, with greater brevity, and significantly more control. With functional template composition, in many cases Solid allows developers to write even less code than Svelte.
23+
Both libraries aim to help their developers write less code but approach it completely differently. Svelte 3 focuses on the optimization of the ease of dealing with localized change focusing on plain object interaction and 2 way binding. In constrast Solid focuses on the data flow by deliberately embraces CQRS and immutable interface. With functional template composition, in many cases Solid allows developers to write even less code than Svelte.
2424

2525
Svelte still represents pushing the boundaries of precompilation where Solid is a bit more conservative offering HyperScript and Tagged Template Literal options in addition to the compiled JSX. But we feel Solid really takes the best of both worlds.
2626

@@ -34,9 +34,7 @@ The biggest difference is that while these libraries do not use the Virtual DOM
3434

3535
Solid is not particularly influenced by Vue, but they are relatable. They both use Proxies in their Reactive system, but that is where the similarities end. Vue's fine grained dependency detection just feeds into a less fine-grained Virtual DOM and Component system whereas Solid keeps its granularity right down to its direct DOM updates.
3636

37-
Vue works off configuration objects where Solid uses a more functional approach instead opting for composeable primitives. Vue credits its configuration as an easy learning curve, but we feel having a few simple primitives actually reduces mental overhead in a similar way. Where configuration objects or lifecycles require learning and remember how they apply(like a checklist), Solid's primitives are not unlike understanding what Array.map does. Once you have the tool you can do the job.
38-
39-
Vue also is setup for direct mutation and 2 way binding, which helps keep the code small. However, Solid's functional approach often leads to less setup than Vue's configuration objects.
37+
Vue values easiness where Solid values transparency. Although Vue's new direction with Vue 3 aligns more with the approach Solid takes. These libraries might align more over time depending on how they continue to evolve.
4038

4139
## RxJS
4240

documentation/components.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ const DynamicComponent = ({ name }) => <div>{ name() }</div>
5353

5454
Solid handles JSX Children similar to React. A single child is a single value on `props.children` and multiple is an array.
5555

56-
## LifeCycle
56+
## Lifecycle
5757

58-
Solid's Components are the key part of its performance. Solid's approach is "Vanishing" Components made possible by lazy prop evaluation. Instead of evaluating prop expressions immediately and passing in values, execution is deferred until the prop is accessed in the child. In so we defer execution until the last moment typically right in the DOM bindings maximizing performance. This flattens the hierarchy and removes the need to maintain a tree of Components. Instead of lifecycles you can use Solid's primitives to express powerful dynamic behaviour.
58+
Solid's Components are the key part of its performance. Solid's approach is "Vanishing" Components made possible by lazy prop evaluation. Instead of evaluating prop expressions immediately and passing in values, execution is deferred until the prop is accessed in the child. In so we defer execution until the last moment typically right in the DOM bindings maximizing performance. This flattens the hierarchy and removes the need to maintain a tree of Components. Instead of lifecycles in Solid are tied to the lifecycle of the reactive system.
5959

6060
## Web Components
6161

documentation/context.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Context
22

3-
Solid has Context API for dependency injection which comprises of createContext, Provider control flow, and useContext. createContext lets you create the Context Object. When Solid renders a component that subscribes to this Context object it will read the current context value from the closest matching Provider above it in the tree. If there is not provider above it will use the default value.
3+
Solid has Context API for dependency injection which comprises of `createContext`, `Provider` control flow, and `useContext`. `createContext` lets you create the Context Object. When Solid renders a component that subscribes to this Context object it will read the current context value from the closest matching Provider above it in the tree. If there is not provider above it will use the default value.
44

55
Example below using Solid's own state mechanism to create a global store that wraps the whole app. Notice that the `Nested` component can access the counter without it being passed down via Props. While you could arguably use a singleton, this pattern gives clear ownership and allows hierarchical store contexts. You could use stores in this case for common data concerns that could appear multiple times on the same page like pagination, scrolling, etc, and have child components that know how to interact with their associated stores. This is a very powerful pattern as you compose patterns that wrap Solid's primitives and Context to create reusable injectable multi component driving modules.
66

documentation/reactivity.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Reactivity
2+
3+
## Signals
4+
5+
Signals are the glue that hold the library together. They often are invisible but interact in very powerful ways that you get more familiar with Solid they unlock a lot of potential.
6+
7+
Signals are a simple primitive that contain values that change over time. With Signals you can track sorts of changes from various sources in your applications. Solid's State object is built from a Proxy over a tree of Signals. You can update a Signal manually or from any Async source.
8+
9+
```js
10+
import { createSignal, onCleanup } from "solid-js";
11+
12+
function useTick(delay) {
13+
const [getCount, setCount] = createSignal(0),
14+
handle = setInterval(() => setCount(getCount() + 1), delay);
15+
onCleanup(() => clearInterval(handle));
16+
return getCount;
17+
}
18+
```
19+
20+
## Accessors Reactive Scope
21+
22+
Signals are special functions that when executed return their value. In addition they are trackable when executed under a reactive scope. This means that when their value read (executed) the currently executing reactive scope is now subscribed to the Signal and will re-execute whenever the Signal is updated.
23+
24+
This mechanism is based on executing function scope so Signals reads can be composed and nested as many levels as desired. By wrapping a Signal read in a thunk `() => signal()` you have effectively created a higher-order signal that can be tracked as well. These accessors are just functions that can be tracked and return a value. No additional primitive or method is needed for them to work as Signals in their own right. However, you need another primitive to make Signals reactive:
25+
26+
## Computations
27+
28+
An computation is calculation over a function execution that automatically dynamically tracks any child signals. A computation goes through a cycle on execution where it releases its previous execution's dependencies, then executes grabbing the current dependencies.
29+
30+
There are 2 main computations used by Solid: Effects which produce side effects, and Memos which are pure and designed to cache values until their reactivity forces re-evaluation.
31+
32+
```js
33+
import { createSignal, createEffect, createMemo } from "solid-js";
34+
35+
const [count, setCount] = createSignal(1),
36+
doubleCount = createMemo(() => count() / 2)
37+
createEffect(() => console.log(doubleCount()));
38+
setCount(count() + 1);
39+
40+
// 2
41+
// 4
42+
```
43+
44+
Keep in mind memos are only necessary if you wish to prevent re-evaluation when the value is pulled. Useful for expensive operations like DOM Node creation. Any example with a memo could also just be a function and effectively be the same without caching.
45+
46+
```js
47+
import { createSignal, createEffect } from "solid-js";
48+
49+
const [count, setCount] = createSignal(1),
50+
doubleCount = () => count() / 2
51+
// No memo still works
52+
createEffect(() => console.log(doubleCount()));
53+
setCount(count() + 1);
54+
55+
// 2
56+
// 4
57+
```
58+
59+
Memos also pass the previous value on each execution. This is useful for reducing operations (obligatory Redux in a couple lines example):
60+
61+
```js
62+
const reducer = (state, action = {}) => {
63+
switch (action.type) {
64+
case "LIST/ADD":
65+
return { ...state, list: [...state.list, action.payload] };
66+
default:
67+
return state;
68+
}
69+
};
70+
71+
// redux
72+
const [getAction, dispatch] = createSignal(),
73+
getStore = createMemo(state => reducer(state, getAction()), { list: [] });
74+
75+
// subscribe and dispatch
76+
createEffect(() => console.log(getStore().list));
77+
dispatch({ type: "LIST/ADD", payload: { id: 1, title: "New Value" } });
78+
```
79+
80+
That being said there are plenty of reasons to use actual Redux.
81+
82+
## Rendering with Reactivity
83+
84+
Solid makes use of it's reactive lifecycle to render the DOM. Creating and updating the DOM are seen as side effects of the reactive system and the tree is constructed by nesting Computations wrapping each binding and dynamic insert with one. You can view its execution like a stack where only the top-most computation is tracking at a given time, and so is the only one tracking the reactive change. Since attributes and inserts are tracked separately from the parent scope responsible for rendering a Component in the first place, updates to attributes or downstream nodes do not require the parent to re-evaluate. If the parent ever were it would wipe out all the children and start again. However the only thing that would make that happen is if something upstream changed, like the condition that made it render in the first place. In so when in the synchronous execution path you are always under a reactive context even if it is not tracking (like a `root`).
85+
86+
## Cleanup
87+
88+
While Solid does not have Component lifecyles in the traditional sense, it still needs to handle cleaning up subscriptions. The way Solid works is that each nested computation is owned by it's parent reactive scope. In so all commputations must be created as part of a root. This detail is generally taken care of for you as the `render` method contains a `createRoot` call. But it can be called directly for cases where it makes sense.
89+
90+
Once inside a scope whenever the scope is re-evaluated or disposed of itself, all children computations will be disposed. In addition you can register a `onCleanup` method that will execute as part of this disposal cycle.
91+
92+
Note: _Solid's graph is synchronously executed so any starting point that isn't caused by a reactive update (perhaps an asynchronous entry) should start from its own root. There are other ways to handle asynchronicity as shown in the [Suspense Docs](./supense.md)
93+
94+
## Composition
95+
96+
State and Signals combine wonderfully as wrapping a state selector in a function instantly makes it reactive accessor. They encourage composing more sophisticated patterns to fit developer need.
97+
98+
```js
99+
// deep reconciled immutable reducer
100+
const useReducer = (reducer, init) => {
101+
const [state, setState] = createState(init),
102+
[getAction, dispatch] = createSignal();
103+
createDependentEffect(
104+
(prevState = init) => {
105+
let action, next;
106+
if (!(action = getAction())) return prevState;
107+
next = reducer(prevState, action);
108+
setState(reconcile(next));
109+
return next;
110+
},
111+
[getAction]
112+
);
113+
return [state, dispatch];
114+
};
115+
```
116+
117+
## Operators
118+
119+
Solid provides a couple simple operators to help construct more complicated behaviors. They work both as standalone and curried Functional Programming form, where they return a function that takes the input accessor. They are not computations themselves and are designed to be passed into a computation. The possibilities of operators are endless. Solid only ships with a base array mapping one:
120+
121+
### `mapArray(() => any[], iterator: (item, index) => any, options: { fallback: () => any }): () => any[]`
122+
123+
### `mapArray(iterator: (item, index) => any, options: { fallback: () => any }): (signal) => () => any[]`
124+
125+
The `solid-rx` package contains more operators that can be used with Solid.

documentation/rendering.md

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
# JSX Rendering
1+
# Rendering
2+
3+
Solid supports templating in 3 forms JSX, Tagged Template Literals, and Solid's HyperScript variant. Although JSX is the predominate form. Why? JSX is a great DSL made for compilation. It has clear syntax, supports TypeScript, works with Babel, supports other tooling like Code Syntax Highlighting and Prettier. It was only pragmatic to use a tool that basically gives you that all for free. As a compiled solution it provides great DX. Why struggle with custom Syntax DSLs when you can use one so widely supported?
4+
5+
Still there is some confusion as to what JSX is and is not. JSX is an XML-like syntax extension to EcmaScript (https://facebook.github.io/jsx/). It is not a language or runtime. Those can be refered to as HyperScript. So while Solid's JSX and might resemble React it by no means works like React and there should be no illusions that a JSX library will just work with Solid. Afterall, there are no JSX libraries, as they all work without JSX, only HyperScript or React ones.
6+
7+
## JSX Compilation
28

39
Rendering involves precompilation of JSX templates into optimized native js code. The JSX code constructs:
410

@@ -32,9 +38,9 @@ render(() => <App />, document.getElementById("main"));
3238

3339
## Events
3440

35-
`on_____` handlers are event handlers expecting a function. The compiler will delegate events where possible (Events that can be composed and bubble) else it will fall back to Level 1 spec "on**\_**" events.
41+
`on_____` handlers are event handlers expecting a function. The compiler will delegate events where possible (Events that can be composed and bubble) else it will fall back to Level 1 spec "on_____" events.
3642

37-
If you wish to bind a value to your delegated event pass an array handler instead and the second argument will be passed to your event handler as the first argument (the event will be second). This can improve performance in large lists.
43+
If you wish to bind a value to events pass an array handler instead and the second argument will be passed to your event handler as the first argument (the event will be second). This can improve performance in large lists when the event is delegated.
3844

3945
```jsx
4046
function handler(itemId, e) {/*...*/}
@@ -136,7 +142,7 @@ _Note these are designed to handle more complex scenarios like Component inserti
136142

137143
## Refs
138144

139-
Refs come in 2 flavours. `ref` which directly assigns the value, and `forwardRef` which calls a callback `(ref) => void` with the reference. To support forwarded properties on spreads, both `ref` and `forwardRef` are called as functions.
145+
Refs come in 2 flavours. `ref` which directly assigns the value, and which calls a callback `(ref) => void` with the reference.
140146

141147
### `ref`
142148

@@ -160,13 +166,11 @@ function App() {
160166
}
161167
```
162168

163-
### `forwardRef`
164-
165-
This form expects a function like React's callback refs. Original use case is like described above:
169+
Callback form expects a function like React's callback refs. Original use case is like described above:
166170

167171
```jsx
168172
function MyComp(props) {
169-
return <div forwardRef={props.ref} />;
173+
return <div ref={props.ref} />;
170174
}
171175

172176
function App() {
@@ -176,15 +180,15 @@ function App() {
176180
}
177181
```
178182

179-
You can also apply `forwardRef` on a Component:
183+
You can also apply `ref` on a Component:
180184

181185
```jsx
182186
function App() {
183-
return <MyComp forwardRef={ref => console.log(ref.clientWidth)} />;
187+
return <MyComp ref={ref => console.log(ref.clientWidth)} />;
184188
}
185189
```
186190

187-
This just passes the function through as `props.ref` again and work similar to the example above except it would run synchronously during render. You can use this to chain as many `forwardRef` up a Component chain as you wish.
191+
This just passes the function through as `props.ref` again and work similar to the example above except it would run synchronously during render. You can use this to chain as many `ref` up a Component chain as you wish.
188192

189193
## Server Side Rendering (Experimental)
190194

0 commit comments

Comments
 (0)