GithubHelp home page GithubHelp logo

solidjs / solid Goto Github PK

View Code? Open in Web Editor NEW
31.0K 216.0 868.0 13.9 MB

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

Home Page: https://solidjs.com

License: MIT License

JavaScript 28.33% TypeScript 71.45% CSS 0.22%
javascript performance solid proxies declarative fine-grained jsx reactive

solid's Introduction

SolidJS

Build Status Coverage Status

NPM Version Discord Subreddit subscribers

WebsiteAPI DocsFeatures TutorialPlaygroundDiscord

Solid is a declarative JavaScript library for creating user interfaces. Instead of using a Virtual DOM, it compiles its templates to real DOM nodes and updates them with fine-grained reactions. Declare your state and use it throughout your app, and when a piece of state changes, only the code that depends on it will rerun.

At a Glance

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

function Counter() {
  const [count, setCount] = createSignal(0);
  const doubleCount = () => count() * 2;
  
  console.log("The body of the function runs once...");

  return (
    <>
      <button onClick={() => setCount(c => c + 1)}>
        {doubleCount()}
      </button>
    </>
  );
}

render(Counter, document.getElementById("app")!);

Try this code in our playground!

Explain this!
import { createSignal } from "solid-js";
import { render } from "solid-js/web";

// A component is just a function that returns a DOM node
function Counter() {
  // Create a piece of reactive state, giving us a accessor, count(), and a setter, setCount()
  const [count, setCount] = createSignal(0);
  
  //To create derived state, just wrap an expression in a function
  const doubleCount = () => count() * 2;
  
  console.log("The body of the function runs once...");

  // JSX allows you to write HTML within your JavaScript function and include dynamic expressions using the { } syntax
  // The only part of this that will ever rerender is the count() text.
  return (
    <>
      <button onClick={() => setCount(c => c + 1)}>
        Increment: {doubleCount()}
      </button>
    </>
  );
}

// The render function mounts a component onto your page
render(Counter, document.getElementById("app")!);

Solid compiles your JSX down to efficient real DOM updates. It uses the same reactive primitives (createSignal) at runtime but making sure there's as little rerendering as possible. Here's what that looks like in this example:

import { template as _$template } from "solid-js/web";
import { delegateEvents as _$delegateEvents } from "solid-js/web";
import { insert as _$insert } from "solid-js/web";
//The compiler pulls out any static HTML
const _tmpl$ = /*#__PURE__*/_$template(`<button>Increment: `);

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

function Counter() {
  const [count, setCount] = createSignal(0);
  
  const doubleCount = () => count() * 2;
  
  console.log("The body of the function runs once...");
  
  return (() => {
    //_el$ is a real DOM node!
    const _el$ = _tmpl$();
    _el$.$$click = () => setCount(c => c + 1);
     //This inserts the count as a child of the button in a way that allows count to update without rerendering the whole button
    _$insert(_el$, doubleCount);
    return _el$;
  })();
}
render(Counter, document.getElementById("app"));
_$delegateEvents(["click"]);

Key Features

  • Fine-grained updates to the real DOM
  • Declarative data: model your state as a system with reactive primitives
  • Render-once mental model: your components are regular JavaScript functions that run once to set up your view
  • Automatic dependency tracking: accessing your reactive state subscribes to it
  • Small and fast
  • Simple: learn a few powerful concepts that can be reused, combined, and built on top of
  • Provides modern framework features like JSX, fragments, Context, Portals, Suspense, streaming SSR, progressive hydration, Error Boundaries and concurrent rendering.
  • Naturally debuggable: A <div> is a real div, so you can use your browser's devtools to inspect the rendering
  • Web component friendly and can author custom elements
  • Isomorphic: render your components on the client and the server
  • Universal: write custom renderers to use Solid anywhere
  • A growing community and ecosystem with active core team support
Quick Start

You can get started with a simple app by running the following in your terminal:

> npx degit solidjs/templates/js my-app
> cd my-app
> npm i # or yarn or pnpm
> npm run dev # or yarn or pnpm

Or for TypeScript:

> npx degit solidjs/templates/ts my-app
> cd my-app
> npm i # or yarn or pnpm
> npm run dev # or yarn or pnpm

This will create a minimal, client-rendered application powered by Vite.

Or you can install the dependencies in your own setup. To use Solid with JSX (recommended), run:

> npm i -D babel-preset-solid
> npm i solid-js

The easiest way to get set up is to add babel-preset-solid to your .babelrc, babel config for webpack, or rollup configuration:

"presets": ["solid"]

For TypeScript to work, remember to set your .tsconfig to handle Solid's JSX:

"compilerOptions": {
  "jsx": "preserve",
  "jsxImportSource": "solid-js",
}

Why Solid?

Performant

Meticulously engineered for performance and with half a decade of research behind it, Solid's performance is almost indistinguishable from optimized vanilla JavaScript (See Solid on the JS Framework Benchmark). Solid is small and completely tree-shakable, and fast when rendering on the server, too. Whether you're writing a fully client-rendered SPA or a server-rendered app, your users see it faster than ever. (Read more about Solid's performance from the library's creator.)

Powerful

Solid is fully-featured with everything you can expect from a modern framework. Performant state management is built-in with Context and Stores: you don't have to reach for a third party library to manage global state (if you don't want to). With Resources, you can use data loaded from the server like any other piece of state and build a responsive UI for it thanks to Suspense and concurrent rendering. And when you're ready to move to the server, Solid has full SSR and serverless support, with streaming and progressive hydration to get to interactive as quickly as possible. (Check out our full interactive features walkthrough.)

Pragmatic

Do more with less: use simple, composable primitives without hidden rules and gotchas. In Solid, components are just functions - rendering is determined purely by how your state is used - so you're free to organize your code how you like and you don't have to learn a new rendering system. Solid encourages patterns like declarative code and read-write segregation that help keep your project maintainable, but isn't opinionated enough to get in your way.

Productive

Solid is built on established tools like JSX and TypeScript and integrates with the Vite ecosystem. Solid's bare-metal, minimal abstractions give you direct access to the DOM, making it easy to use your favorite native JavaScript libraries like D3. And the Solid ecosystem is growing fast, with custom primitives, component libraries, and build-time utilities that let you write Solid code in new ways.

More

Check out our official documentation or browse some examples

Browser Support

SolidJS Core is committed to supporting the last 2 years of modern browsers including Firefox, Safari, Chrome and Edge (for desktop and mobile devices). We do not support IE or similar sunset browsers. For server environments, we support Node LTS and the latest Deno and Cloudflare Worker runtimes.

Testing Powered By SauceLabs

Community

Come chat with us on Discord! Solid's creator and the rest of the core team are active there, and we're always looking for contributions.

Contributors

Open Collective

Support us with a donation and help us continue our activities. [Contribute]

Sponsors

Become a sponsor and get your logo on our README on GitHub with a link to your site. [Become a sponsor]

solid's People

Contributors

aminya avatar amoutonbrady avatar bikeshedder avatar bluenote10 avatar cynecx avatar davedbase avatar dependabot[bot] avatar edemaine avatar exelord avatar gavin-gong avatar jorroll avatar juanrgm avatar jutanium avatar kestrer avatar liquidproquo avatar lxsmnsyc avatar mdynnl avatar modderme123 avatar mrfoxpro avatar neodon avatar nksaraf avatar orenelbaum avatar otonashixav avatar ryansolid avatar seneca avatar steve8708 avatar thetarnav avatar tombyrer avatar trusktr avatar zsparal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

solid's Issues

React Parity: Components & Context

For me this is the last question that is hanging on me so I'm looking for feedback suggestions. I've implemented JSX Fragments, control flow with fallbacks, Portals, and Suspense. I've had a lot of fun solving similar problems to React but in completely different ways that are Web Component friendly and work with Fine Grained performance considerations. I feel now, most typical cases are covered and you can do some pretty amazing things.

But ultimately, a library like Solid does not need Components to work, and in so upper case Function Components are essentially just Function templates. There are no context boundaries, no internalized state etc. This is definitely the biggest departure for those familiar with React.

Components vs Templates

If you want to pass state variables to these templates you need to essentially pass the whole state object or wrap in function accessors to get fine grained resolution.

const Child = ({ getName }) => (
  <div>{( getName() )}</div>
);

const App = () => {
  const [state, setState] = createState({ name: 'John' });
  return <Child getName={() => state.name} />
}

With WebComponents this isn't a problem since children will be bound as DOM elements:

const App = () => {
  const [state, setState] = createState({ name: 'John' });
  return <child-elem name={( state.name )} />
}

Where does that leave us? I can simplify binding syntax to use the similar approach to auto wrap but the child template still is dealing with a function. I can always wrap all props in a state variable and create computations based on the binding to update that state behind the scenes. But the overhead of doing the same on simple Function components seems awkward. I'm not sure I like creating these arbitrary Component boundaries as it sidesteps the power of a library like Solid. That the UI or Code modularity considerations are independent of your ability to optimally manage data flow.

Dependency Injection

Solid Components has a Context API but Solid does not. The context in Solid is the computation or Root context which is not tied to a specific DOM node necessarily. While this can work like a typical framework like React it wouldn't play nice with Web Components. So it makes sense to tie context to elements but Solid Template Functions are independent of those boundaries.

It goes further than this as I can use control flow to inject context providers fairly easily but the reading(consumer) gets much more complicated as the current executing code, which is independent of it's place in the hierarchy, needs to know it's got the right context. The only constant is its owning context or the Elements being rendered at that scope of templating. What ultimately comes down to is that Fine Grained regardless of how nested the tree, flattens the problem. This is something I used to my advantage in Sierpinski Triangle Demo to show really controlled finite Asynchronous rendering to provide ultimate control and performance, but it bites us here pretty hard.

There are other ways to solve DI like import global registries as a variation on the Singleton where Dependencies are constructed and registered. This method is just as controllable for testing and is in the majority effectively the same as using hierarchical look up but it is definitely not as nice. I can fake it behind a lovely API that makes it looks just like React and no one would be the wiser, well until they tried to nest contexts of the same type in multi levels of the tree.

I think I'm just not as familiar with what Angular or other non-React libraries do here so I'm hoping investigation lends to a good option.

Debugging

I'm fairly happy with Debugging since the compiled code hides the complications of triggers but exposes the direct imperative DOM operations and node creation, so by dropping breakpoints right in your implementation code you can very transparently see what is going on. Much more so than with a Virtual DOM or earlier non-compiled Fine Grained libraries. However, working around those restrictions I feel VDOM libraries have come up with really clever ways to improve the debugging experience. Even through browser tooling. I'd love to know what sort of debugging experience elements you really like from your experience or anything you would like to see with Solid. I played with the idea of Chrome Extension but mapping mapping the Real Dom back to Components or Context didn't feel very useful. It's possible visualization like Cycle.js/RxJS is the better option here. This is an area that earlier Fine Grained predecessors have been pretty poor in tooling.


In any case any thoughts on these topics would be much appreciated. I feel these are the final pieces to really solidify (excuse the pun) what the 1.0.0 release would look like.

`TypeError: marker is null` when returning empty strings

The following example throws a TypeError: marker is null on the second click on the button:

const App = () => {
  const [state, setState] = createState({enabled: false})
  return (
    <div>
      <div>Enabled: {(state.enabled ? "yes" : "")}</div>
      <button onclick={(event) => setState({enabled: !state.enabled})}>Toggle</button>
    </div>
  )
};

The problem seems to be caused by using an empty string literal "" in the state.enabled === false case, because changing it to a non-empty literal works. Interestingly it also works on the initial rendering, just not on a re-render.

I should probably use <$> for conditional rendering in general, but I would have expected that the empty string is still handled correctly.

Typescript definitions

Hi! Thanks for sharing your work with us! I was wondering if you have any plans to provide typescript definitions? I started to really like typescript, but I am not yet capable of writing definitions for a complex framework like yours.

[idea] declarative JS

JSX is nice when we have tooling, but we may not want to run Babel in production if we don't have tooling.

Maybe it would be nice to have some sort of declarative form in JS. For example,

import {header, h1} from 'surplus/elements'

const seconds = S.value(0)
setInterval(() => seconds(seconds() + 1), 1000)

const TodoHeader = ({ addTodo }) => [
    [header, [
        ['class', 'header'],
        [h1, [
            'todos ', seconds,
            ...
        ]]
    ]]
]

or maybe something more explicit with objects, but more indentation:

import {header, h1} from 'surplus/elements'

const seconds = S.value(0)
setInterval(() => seconds(seconds() + 1), 1000)

const TodoHeader = ({ addTodo }) => header({
    class: 'header',
    children: [
        h1({
            children: [
                'todos ', seconds,
            ],
        })
    ]
})

In the end, they'd compile to basically the same thing as the following would:

const seconds = S.value(0)
setInterval(() => seconds(seconds() + 1), 1000)

const TodoHeader = ({ addTodo }) =>
  <header class='header'>
    <h1>todos {seconds}</h1>
    ...
  </header>

Then, JSX would be a convenient way to use HTML-like syntax if that is wanted, and when tooling is available.

Why ES6 Proxies?

Could it be achieved with accessors, and therefore compatible with older browsers? Windows 10 still ships with IE 11, and I bet plenty of people are still not on IE 11.

Is there something specific that they are needed for?

Vue for example, uses getters/setters to make POJO-like simplicity.

RFC: Control Flow Refactor

Summary

This proposal looks to simplify/remove control flow by leveraging Solid's own Component API. Instead of writing:

<$ each={ state.list }>{ item => item }</$>

You'd write

<For each={( state.list )}>{ item => item}</For>

Or if you didn't want to use control flow you could use functional underpinnings that could apply to more than just Rendering:

map(() => state.list, item => item)

Please leave any feedback below. I'd love to have feedback on any of the pieces I am proposing here from syntax to overall objective.

Motivation

When I initially implemented Control Flow it was for many reasons:

  1. I wanted to use the compiler to mask accessor timing from the end user required for managing reactivity properly.
  2. I could generalize the approach in DOM Expressions to make any change push out to companion libraries ko-jsx and mobx-jsx.
  3. I believed by merging the reactivity code with the DOM updates directly I could more efficiently render the DOM.

Ironically all 3 ended up being sort of mute:

  1. Development of the general Component and Context API gives a lot of this potential without a compiler.
  2. This put a lot more pressure on 3rd party observable libraries to implement the same features as Solid. It ended up being more work than it was worth to maintain when the interest for those other libraries are lower.
  3. I did not notice a substantial (or any) performance gain over doing the work in 1 or 2 passes.

In addition this approach had these additional problems:

  • Typescript hates $. It just can't handle that as an intrinsic element. Typing in general is a lot looser when I have a generic flow element.
  • If anything I just made all the code much more complicated. It was harder to do your own control flow since the tools were not exposed in an easier way.

By changing this anyone will be able to create their own Control Flow or not so we can handle scenarios that today I cannot anticipate. And it should reduce overall library size from the reduction of complexity in the Control Flow themselves.

Proposal

  1. DOM Expressions removes Control Flow from its core but instead uses a generic reconciler for all expressions. That way regardless of how the array of nodes or Fragments are passed in the renderer will be able to handle it. There will be a slight overhead for Fragments now in that their childNodes will need to be normalized like any array of data coming in.
  2. Babel Plugin JSX DOM Expressions will offer the ability to mark component methods as BuiltIns. If the compiler comes across them they will auto import them from the module set by the moduleName configuration option.
  3. Solid createComponent (used behind the scenes in JSX DOM Expressions) will autowrap element children(JSX Components and DOM elements) as dynamic properties. This means they will be lazy evaluated on access. props.children accessor will run the children code.
  4. Solid will ship with a map function reconciler that does memoized map projection. This can be used to map any reactive list to another reactive list that updates whenever the initial map indexes change. The For control flow will use this internally.
  5. Solid will ship with Components for the existing Control Flow (For, Show, Switch, Portal, Suspense, Context.Provide)
  6. There will be a babel-preset-solid that will use Babel Plugin JSX DOM Expressions as preconfigured for Solid. This will include a set of builtIns (For, Show, Switch, Portal, Suspense) and the correct moduleName etc.

Control Flow

Provide will not be a Built In but rather exported on the Context object the way it works in React.

For

<For
  each={( state.list )}
  fallback={( <div>Loading...</div> )}
>{ item => 
  <div>{ item }</div>
}</For>

Show

<Show
  when={( state.count > 0 )}
  fallback={( <div>Loading...</div> )}
>
  <div>My Content</div>
</Show>

Switch

<Switch fallback={( <div>Not Found</div> )}>
  <Match when={( state.route === 'home' )}><Home /></Match>
  <Match when={( state.route === 'settings' )}><Settings /></Match>
</Switch>

Portal

<Portal anchor={document.getElementById('modal')}>
  <div>My Content</div>
</Portal>

Suspense

<Suspense fallback={( <div>Loading...</div> )}>
  <AsyncComponent />
</Suspense>

Drawbacks

There are a couple drawbacks:

  1. $ was tighter syntax lending to less verbose code. You could instantly identify control flow on the page. Without pre-compilation we want separate imports (and works better with TypeScript).
  2. Dynamic/Lazy evaluated attributes will need to be wrapped in parens {( )}. Pre-compilation knew exactly which attributes were dynamic. Now that this has opened up you need to specify.

Adoption

It is a breaking change across all libraries. And will be released as a new minor version (since there is no major version). For the most part this will be a simple code change to make in existing Solid code.

RFC: Suspense Aware Control Flow

Summary

The goal of this proposal is to emulate some of the more complicated behaviours of React Suspense so that they can be readily used in Solid. While Solid can do simple delegated asynchronous loading, the ability of React Suspense to hold the previously rendered content even under say switching a tab is incredibly powerful and provides a lot of value. The proposed approach is to plug into Solid Control Flow's existing transform directives to provide an opt-in way to handle Suspense with delayed Fallbacks.

You can see it in action here: https://codesandbox.io/s/solid-suspense-tabs-vkgpj

Motivation

Solid has had support for a basic version of Suspense for several releases now. However, it works completely different than React. Mainly it is that Solid works completely different than React. React renders top down in two passes essentially. First, it executes the render function hierarchy creating a virtual tree, and then it diffs and patches the actual DOM. What this means is that at any point before updating the DOM it can be interrupted, and in the case of Suspense it can be resumed. This is a very simple model when there is only a single thread of execution.

But the real power of React Suspense is how it can hold a frame before the fallback is engaged. In simple scenarios, this is easy to emulate. With pull-to-refresh, or loading the next page, you can defer updating the state until the results of the asynchronous action are done. However, branching decision points, control flow, will update immediately even if a descendant is asynchronous.

Proposal

Solid provides a suspense transform which looks at Suspense state to delay Control Flow updates when Suspense is suspended.

Drawbacks

This approach only works because Solid executes the majority of its code before connecting to the DOM. For instance, if Component A, has a child Component B, who has a child Component C, execution runs roughly like:

  • ComponentA Starts Creation
  • ComponentA Starts Rendering
    • ComponentB Starts Creation
    • ComponentB Starts Rendering
      • ComponentC Starts Creation
      • ComponentC Starts Rendering
      • ComponentC Ends Rendering
      • ComponentC Returns DOM Nodes
    • ComponentB Attaches ComponentC DOM Nodes
    • ComponentB Ends Rendering
    • ComponentB Returns DOM Nodes
  • ComponentA Attaches ComponentB DOM Nodes
  • ComponentA Ends Rendering
  • ComponentA Returns DOM Nodes
  • .....
  • Root Attaches DOM Nodes to DOM

Any branching follows the same pattern where the parent does the insert into the DOM. Adding a delay is trivial. However, any async action done at connection time comes too late to prevent the Control Flow from inserting its new children. For Solid this shouldn't be much of a problem. But Web Components specifically Solid Element often work off connection time since context is due to asynchronous upgrades (no guarentee that constructor is called synchronously) the only guarenteed way to pass context is through the DOM tree after connection. More so the only natural time to handle disposal is disconnectedCallback, which is its natural complement. Connected/Disconnected callbacks can get called multiple times as DOM is manipulated so moving create logic to constructor can be asymmetric. I will be using beta as an opportunity to evaluate scenarios to best handle Web Components.

Adoption

This change is non-evasive and completely opt-in. It will be included as beta looking for more feedback over the next several versions of Solid.

I am interested in any thoughts. Let me know what you think.

Convenient handling of Proxy type?

When using Typescript, I cannot find a nice way to work with the implicit Proxy type. The type inferred from createState wraps objects and arrays into Proxy<T>. Now if I want to pass that field into another component the compiler complains, for instance:

function MyComponent(props: {data: number[]}) {
  return <div>...</div>
}

function App() {
  const [state, setState] = createState({
    data: [1, 2, 3]
  })
  return <MyComponent data={(state.data)}/>
}

Error:

src/typed.tsx:36:23 - error TS2740: Type 'Proxy<number[]>' is missing the following properties from type 'number[]': length, pop, push, concat, and 25 more.

36   return <MyComponent data={(state.data)}/>

The inferred type of state.data is Proxy<number[]> which is obviously correct, but since Proxy is a private type, I cannot pass state.data around or do anything with it without a cast.

As a work-around I can double cast state.someProp as any as TheUnderlyingType everywhere, but that makes working with state quite tedious (for example (state.data as any as number[])[i] instead of just state.data[i]).

I'm wondering

  • Can we somehow convince Typescript that a Proxy<T> really behaves like a T (if that's the case)?
  • Should the type system rather hide the wrapping for convenience?
  • Exporting Proxy would at least allow clients to explicitly use Proxy<T> in type signatures. This may reduce the number of casts required, but eventually a cast is still necessary when actually doing something with T.

Parcel integration problem, r is not defined

Hey there, long time lurker of your project here. First and foremost, I'd like to congratulate you on Solid Js and your other related project.

I'd like to try your library on a side project of mine and for such little experiments, I like to tool around with parcel as my bundler.

For some reasons that I are too advanced for me I believe, it seems like parcel interfere with the way solid is expecting the code to look like. Declaring a node directly in a variable works, but declaring a functional component doesn't seem to work.

import { r } from 'solid-js/dom';

// work
const working = <h1>Hello world</h1>
document.body.appenchChild(working);

// passing r as a prop, works
const Working = ({ name, r }) => <h1>Hello {name}</h1>
document.body.appenchChild(<Working name="world" r={r} />);

// not working
// Error throw: Uncaught ReferenceError: r is not defined
const NotWorking = ({ name }) => <h1>Hello {name}</h1>
document.body.appenchChild(<NotWorking name="world" />);

If I don't pass the r object imported from solid-js/dom it appears that the functional component doesn't have access to it.

If you have a few minutes and if you think it's relevant, I've also set up a git to reproduce the bug here (clone, npm install, npm run dev / build).

Feel free to close the issue if the problem doesn't come from Solid.

Cheers

`npm init solid app <app-name>` fails

npm init solid app my-solid-app
npx: installed 91 in 7.056s

Creating a new Solid app in /home/USER/tmp/my-solid-app.

internal/modules/cjs/loader.js:584
    throw err;
    ^

Error: Cannot find module './yarn.lock.cached'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:582:15)
    at Function.resolve (internal/modules/cjs/helpers.js:30:19)
    at createApp (/home/USER/.npm/_npx/23726/lib/node_modules/create-solid/createSolid.js:254:17)
    at Object.<anonymous> (/home/USER/.npm/_npx/23726/lib/node_modules/create-solid/createSolid.js:169:1)
    at Module._compile (internal/modules/cjs/loader.js:701:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
    at Module.load (internal/modules/cjs/loader.js:600:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
    at Function.Module._load (internal/modules/cjs/loader.js:531:3)
    at Module.require (internal/modules/cjs/loader.js:637:17)

npm/node is installed with nvm

examples on codepen.

It'd be sweet to have some examples on codepen (putting aside the performance impact of client-side compilation).

I suppose it's a matter of getting babel running in the example first.

npm init solid app <app-name> throw an error `Cannot read property 'endsWith' of undefined`

Hello, when i try to start a project with command npm init solid app my-name i've got an error like : `
C:\Sites\solidjs>npm init solid app solid-app-1

Creating a new Solid app in C:\Sites\solidjs\solid-app-1.

C:\Users\User\AppData\Roaming\npm\node_modules\create-solid\createSolid.js:217
const useYarn = process.env._.endsWith('yarn') && shouldUseYarn()
^

TypeError: Cannot read property 'endsWith' of undefined
at createApp (C:\Users\User\AppData\Roaming\npm\node_modules\create-solid\createSolid.js:217:33)
at Object. (C:\Users\User\AppData\Roaming\npm\node_modules\create-solid\createSolid.js:182:1)
at Module._compile (internal/modules/cjs/loader.js:702:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:713:10)
at Module.load (internal/modules/cjs/loader.js:612:32)
at tryModuleLoad (internal/modules/cjs/loader.js:551:12)
at Function.Module._load (internal/modules/cjs/loader.js:543:3)
at Module.require (internal/modules/cjs/loader.js:650:17)
at require (internal/modules/cjs/helpers.js:20:18)
at Object. (C:\Users\User\AppData\Roaming\npm\node_modules\create-solid\index.js:58:1)

i have same issue when i useyarn create solid app `
could anyone help me?/

Typings for setState

Hello,
I was just going through the examples, and ran Counter sample with Typescript and a minimal setup - parcel, babel-preset-solid and got an error here:
setState('counter', c => c + 1); -> c implicitly has any type
if casted to number
setState('counter', (c: number) => c + 1); -> Type 'undefined' is not assignable to type 'number'. which comes from the definition of the StateAtom
so, i had to do this
setState('counter', (c: unknown) => (c as number) + 1); which is kinda awkward. Am I missing something or the definitions could be improved here?

Attribute Ordering

I've been playing around with Solid and came across an issue I found interesting. There is at least one case where an HTML element has an attribute with a dependency on another one of its attributes, which means that the order of their application becomes important.

I've documented some examples here https://codesandbox.io/s/attribute-order-example-pmdqh

The range input <input type="range" /> has a min and max attribute which are taken into account when setting the value. This makes sense, you have to clamp the value set in order to keep it in range.

Essentially, if you apply the value before min and max, you may (depending on the numbers used) get a different outcome than applying the min and max first. This is easy enough for an author to fix by simply changing the ordering in their JSX; however, this brings up a few questions

  • Does the DOM Expressions library guarantee ordering of application at runtime based on the ordering found is the JSX?
  • Is this something the user should have to be aware of? I found the issue trying to create the same simple app in React, Vue and Solid - the others appear to prevent the issue.
  • If a user's code sets some state that updates all of these attributes at the same time (ie setState({ min, max, value });) could this cause a problem? What determines the order they are applied?

I'm not sure what the answers are but it seems like the best solution would be to uncover all of these attribute dependencies (maybe other elements have them too) and take them into account when applying updates, possibly rearranging the calls so the application author and end user have a consistent outcome.

I really like this project and I'm blown away that it seems to have everything I love about React (JSX, functional components, hooks) in such a tiny and speedy library. Good work!

How to clean up?

Once we've made some elements, and things are running, how do we clean them up? Deleting the element from DOM or losing the reference doesn't cause the element to be collected in the following example:

        <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
        <script src="https://unpkg.com/solid-standalone"></script>
        <script type='text/solid'>
            const { useState, root } = Solid;

            const App = () => {
                const [state, setState] = useState({counter: 0});
                setInterval(() => setState('counter', c => c + 1), 1000);
                return <div>{( state.counter )}</div>
            }

            root(() => document.body.appendChild(<App />));
        </script>

It gets compiled to:

const _tmpl$ = document.createElement("template");

_tmpl$.innerHTML = "<div></div>";
const {
  useState,
  root
} = Solid;

const App = () => {
  const [state, setState] = useState({
    counter: 0
  });
  setInterval(() => setState('counter', c => c + 1), 1000);
  return function () {
    const _el$ = _tmpl$.content.firstChild.cloneNode(true);

    SolidDOM.r.insert(_el$, () => state.counter);
    return _el$;
  }();
};

root(() => document.body.appendChild(App()));

so the inner function created by <div>{( state.counter )}</div> for example contains a reference to _tmpl$, etc, and the counter is incremented forever even if all references to the div are lost.

How can we do the same example, but with a way to clean up when finished?

Duplicate edges in signal graph

If I understand correctly, when signal value is accessed, it doesn't perform a check when the edge already exists:

  1. Accessing current() value: https://github.com/ryansolid/solid/blob/8fe0c11360387a325d64e040a756585a69f6da63/src/signals.ts#L189-L192
  2. Creating an edge: https://github.com/ryansolid/solid/blob/8fe0c11360387a325d64e040a756585a69f6da63/src/signals.ts#L405-L439

Without such checks, use cases that involve sorting/filtering in data tables will create insanely huge graphs.

For example, simple sort() on 5000 items will create ~110000 edges:

let i = 0;

class Item {
  _a = Math.random();

  get a() {
    i++;
    return this._a;
  }
}

const items = [];
for (let i = 0; i < 5000; i++) {
  items.push(new Item());
}

items.sort((a, b) => a.a - b.a);
console.log(i);

How to do conditional styling of components

I want to conditionally apply style to an element

import { createState, onCleanup } from "solid-js";
import { render } from "solid-js/dom";

const App = () => {
  const [state, setState] = createState({ count: 0 }),
    timer = setInterval(() => setState("count", c => c + 1), 1000);
  onCleanup(() => clearInterval(timer));

  /* prettier-ignore */
  return <div style={{ backgroundColor: (state.count % 2) ? 'cyan' : 'beige' }}>{( state.count )}</div>;
};

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

codesandbox link: https://codesandbox.io/s/solid-counter-d2e4k

This just sets the backgroundColor to beige only once at start, and does not update.

I got to some workarounds to make it work but they result in complete re-renders of component.

return <div>
    {() => {
      const style = {
        backgroundColor: (state.count % 2 === 0) ? 'beige' : 'cyan',
       };

      return <div style={style}>{(state.count)}</div>
    }}
  </div>;

codesandbox link: https://codesandbox.io/s/solid-counter-kjiup
This one needs a wrapper element around which you want to work.

[Feat] Remove undefined props from HTMLElement

When toggling HTML attributes, it would be nice if it were possible to have an attribute removed from the element if its value is undefined. Right now, undefined is passed as a string, which in some cases breaks the expected functionality.

For example: I have a Video component with an optional prop thumbnail:

// App.js
<Video />

// Video.js
<video poster={ props.thumbnail }>
  ...
</video>

The above examples produces this DOM output:

<video poster="undefined">
  ...
</video>

This causes two problems: no thumbnail is shown and a 404 error is triggered. If the poster attribute were to be removed, these problems go away and additionally it will fallback to display the first frame of the video as a thumbnail.

How to get reference to rendered element after mounting?

I was searching the docs for the best practices for getting a ref to an element rendered by Solid. Use case: Passing an element reference to an external library.

I could probably just give it an id, create an effect, and use getElementById, but that looks like an anti-pattern in Solid.

The afterRender directive sounds like it does the job, but from the docs I cannot infer how it works, and it only exists for when and each. Is there something similar for an unconditional <div>?

Regarding getting the element reference I could simple use:

// example use of a plotting library
function Example() {
  let el = <div></div>
  // pass el to external library
  return el;
}

but than I'm missing lifecycle handling, i.e., the external library may require to receive el only after it gets mounted. The use case is plotting libraries that can only plot into the given element after the element has become visible, because they perform internal size measurements.

performance considerations with immutable data (using S.js variables instead of a state object)

Thanks for the description at #2. I was going to make another issue, the topic of which you talked about there.

My use case for custom elements (and libs like this one) is to make element for declaratively describing 3D scenes.

When animating things, I want to try to keep memory from growing as much as possible. I was concerned about the immutable state changes leaving the garbage collector with many objects to collect after updating properties every frame of an animation and thus making the graphics stutter.

(imagine graphically rich web games)

In the other thread you mentioned

You can technically use Solid renderer without the State object with S.js signals and it would be more performant (and be hard to tell apart from Surplus)

Interesting! That makes me curious about using solid with S.js and not using the state API. Can you make a small example snippet of using S.js signals instead of the state API?

About the "each" directive time complexity when updating

For example this code in rendering.md:

<ul>
  <$ each={ state.users }>{
    user => <li>
      <div>{( user.firstName )}</div>
      <$ when={ user.stars > 100 }>
        <div>Verified</div>
      </$>
    </li>
  }</$>
</ul>

The only way to change state.users for solid.js is making a new array, so time complexity is O(n):

setState({
   users: [...state.users, { firstName: "Tom", stars: 10 } ] // clone state.users and add a new element
})

And because of this, solid.js has to scan old array and new array to know what dom element to remove and what dom element to add, so the time complexity is also O(n).
So can I do anything to lower this, like the "vanilajs" implementation on https://github.com/krausest/js-framework-benchmark which has O(1) complexity for updating an element in array?

Unexpected behavior in list reconciliation

I'm not sure what is a minimal reproducing example of this issue, but I can reproduce it as follows (codesandbox):

import { createState } from "solid-js";
import { render, For } from "solid-js/dom";

const App = () => {
  const [state, setState] = createState({
    data: ["a", "b", "c"],
    selectedIndex: -1
  });

  setTimeout(() => {
    setState({
      selectedIndex: 1
    });
  }, 1000);

  setTimeout(() => {
    setState({
      data: ["b"],
      selectedIndex: 0
    });
  }, 2000);

  /* prettier-ignore */
  return (
    <div>
      <div>Selected Index: {(state.selectedIndex)}</div>
      <For each={(state.data)}>{ (x, i) => 
        <div class={(i === state.selectedIndex ? "selected" : "")}>
          {x}
        </div>
      }</For>
    </div>
  );
};

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

Behavior:

  • at t = 1 sec the selected index is set to 1, selecting element b (visible border).
  • at t = 2 sec the data updates to just ["b"] adjusting the selected index to 0. Thus, element b should still have a border, but it doesn't.

This issue might by related to #50, because it is also caused by having identical nodes: If the data update at t = 2 sec sets the data to ["x"], the item gets selected properly.

I have experienced this issue in a "search as you type" component with item selection (that's why narrowing down the item list is the typical behavior). In my app there can be all sorts of funky states, including off-by-one selections, or multiple selected items. I'm not sure if these are multiple issues, or just manifestations of the same problem. Or if I'm doing something wrong ;).


Side note: The TypeScript definitions of <For> currently do not allow functions of the type (item: T, i: number), but the underlying map function does. This allowed me to define a <ForIndexed> on user site. It's really nice that the new control flow elements are fully accessible compared to <$> and can easily extended on user site.

Can't use Date Object in state?

Hello, for some reason this simple code (I use your Counter example) throw error when I'm trying to use Date Object method from Solidjs state:

<html>
  <head>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script src="https://unpkg.com/solid-standalone"></script>
    <script type='text/solid'>
      const { createRoot, createState, onCleanup } = Solid;
      
      const App = () => {
        const [state, setState] = createState({ counter: 0, dateTime: new Date() }),
          timer = setInterval(() => setState({ counter: state.counter + 1 }), 1000);
        onCleanup(() => clearInterval(timer));
        return <div>{( state.counter.toString() + ' ' + state.dateTime.toString() )}</div>
      }
      
      createRoot(() => document.body.appendChild(<App />));
    </script>
  </head>
  <body>
  </body>
</html>

Console error:

Uncaught TypeError: Method Date.prototype.toString called on incompatible receiver [object Object]

Maybe the method call isn't bound to the correct object?

JSX type definitions

I'm still assessing Solid's Typescript compatibility and I noticed that there are no type definitions for e.g. JSX.IntrinsicElements. This prevents using Typescript in strict mode, in particular it enforces to allow implicit any.

In strict mode the following fails to compile:

import { createRoot } from 'solid-js';
let el = document.getElementById('root')!;
createRoot(() => el.appendChild(<div>Test</div>));

Error at the <div>Test</div> expression:

JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.

To solve this I think we need definitions similar to these for React.

Unexpected cleanup without a root warning

Just a low priority question. I often get "cleanup with a root warnings" although I'm not using cleanup in my code. I have broken it down to a small example like this (codesandbox):

import { render, For } from 'solid-js/dom';

const App = () => {
  return (
    <For each={[1, 2, 3]}>{ i =>
      <div>{i * 10}</div>
    }</For>
  );
};

let el = document.getElementById('app')!;
let app = <App/>
render(() => app, el);

In this example the warning disappears when moving the <App/> constructor into the render call, or by not using <For>. In more complex apps I haven't understood when/why I get the warning and how to get rid of it.

Is this something to worry about?

Issue with conditional rendering of fragments

It looks like there is an issue with conditional rendering of fragments. The following example produces a correct DOM on the first rendering of the fragment, but rendering it a second time modifies the DOM in an unexpected way:

const App = () => {
  const [state, setState] = createState({
    switch: true
  });
  let a = <div>a</div>;
  let b = (
    <>
      <div>b</div>
    </>
  );
  return (
    <div>
      <button onclick={(event) => setState({switch: !state.switch})}>click</button>
      {( state.switch ? a : b )}
    </div>
  );
};

Workaround for /* prettier-ignore */

This is not an issue, just a suggestion.

Instead of telling prettier to ignore code, change the statement:

{(state.counter)}

to

{(void 0, state.counter)}

This is compatible with prettier and even with typescript.

Combining control flow operators

Does Solid support combining control flow operators? I can't get it to work, not sure what I'm doing wrong. I removed code bit by bit and I think this example captures where the error occurs:

import { createState, createRoot, onCleanup } from 'solid-js'

const CountingComponent = () => {
  const [state, setState] = createState({ counter: 0, data: [1, 2, 3], flag: 3 });

  const interval = setInterval(() => {
    setState({ counter: state.counter + 1, flag: state.counter % 3 })
  }, 1000);

  onCleanup(() => clearInterval(interval));

  return (
    <$ each={state.data} >
      <>
        <$ when={state.flag === 1}>
          <div>
            Flag is 1.
          </div>
        </$>
        <$ when={state.flag === 2}>
          <span>
            Flag is 2.
          </span>
        </$>
      </>
    </$>
  )
}

createRoot(() =>
  document
    .getElementById("app")
    .appendChild(<CountingComponent />)
);

Once data has been rendered once, it throws the same error 3 times (cannot read property nextSibling of null, in removeNodes), then goes on to get the next error (cannot read property __rGroup of null), where it enters a steady state and keeps throwing this error.

The error is thrown when trying to dismount the last item from state.data. The childnodes of .app are then [#comment, div, #comment], and it passes in the last comment to removeNodes and null as the end marker. Looks like it's either sending in a remaining comment node that should have been removed from a previous item, or passing in the wrong comment node? Not sure.

reversing order of operator?

I see the TodoMVC example has some code like

{when(() => <TodoFooter {...props} />)(() => props.state.todos.length > 0)}

but it seems like it would be easier to read if it were

{when(() => props.state.todos.length > 0)(() => <TodoFooter {...props} />)}

Is that possible to make when be that way without performance impact?

Similarly with each, I feel like it'd be nicer to read if it were

      each(() => filterList(state.todos))(todo =>
        <TodoItem {...props} todo={todo} />
      )

Should I consider Submitting a Signal version to JS Frameworks Benchmark?

Sort of torn since I like to show off the ergonomics of Solid and its compact State object. However, I do have a faster implementation that is by no means anymore hacky using pure Signals that removes the proxy overhead from the execution, and is lower in memory usage and faster in performance. It could be enough to take top spot on the benchmark. But is it worth it to show off the raw speed of this approach?

A place for a Solid community ?

Hello,

Solid seems really interesting to me and despite the articles, the documentation, I think there is a place missing for a nascent community, a place to exchange, discuss, question about the future of the framework, its use etc.

Such a space could well be a mailing list or a forum like Discourse... That said, perhaps the easiest way would be to create a Gitter chat room, directly related to code repositories? As an alternative, there are these Github issues, but I'm not sure that's appropriate. What do you think?

wording: "onCleanup" vs "useCleanup", "sideEffect" vs "useEffect", etc

Could it be more semantic to have functions like useCleanup and useEffect named onCleanup and sideeffect (and similar consideration for other functions)?

f.e.

    const interval = setInterval(() => setState('counter', c => c + 1), 1000);
    onCleanup(() => clearInterval(interval))
    sideEffect(() => console.log(state.counter))

`TypeError: node is undefined` when updating with <$ each>

I'm not sure if I'm doing something wrong here, all my attempts to use <$ each=...> syntax eventually result in runtime errors "node is undefined". A minimal example would be:

export function Debug() {
  const [state, setState] = createState({
    data: [],
  })

  createEffect(() => {
    setTimeout(() => {
      setState({data: ["A", "B"]})
    }, 100)
  })

  /*
  // using this return works fine
  return (
    <div>
      {(state.data.map(item => <div>{item}</div>))}
    </div>
  )
  */
  // but this doesn't
  return (
    <div>
      <$ each={(state.data)}>
        { item => {
          <div>{item}</div>
        }}
      </$>
    </div>
  )
}

The traceback points to this line.

For doesn't create identical elements

I noticed that For doesn't want to create the same repeated element multiple times. The following example (codesandbox) should output multple divs all showing "new element", but only one element gets render:

import { createState, createEffect } from "solid-js";
import { render, For } from "solid-js/dom";

const App = () => {
  const [state, setState] = createState({
    list: []
  });

  setInterval(() => {
    setState({
      list: state.list.concat(["new element"])
    });
  }, 1000);

  createEffect(() => {
    console.log(state.list);
  });

  /* prettier-ignore */
  return (
    <For each={(state.list)}>
      {(item) => <div>{item}</div>}
    </For>
  );
};

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

If I give the elements unique values it works.

Typescript starter project?

Is there any starter project showing how to use Solid with Typescript? I'm not a web developer so initial project setup is always tedious.

I was using npm init solid app ... but it hides the entire transpile/bundle setup behind solid-scripts start. What is the best way to integrate Typescript transpilation into that?

Effects of setState not working under certain conditions

I ran into some more weird update problems in my app. During debugging the problem has again morphed into something different, but hopefully it still represents my original issue. Example (codesandbox):

import { createState, createEffect } from "solid-js";
import { render, Switch, Match } from "solid-js/dom";

const Title = props => {
  /* prettier-ignore */
  return <div>Title length: {(props.title.length)}</div>;
};

const App = () => {
  const [state, setState] = createState({
    title: null,
    show: false
  });

  createEffect(() => {
    console.log("show state:", state.show);
  });

  /* prettier-ignore */
  return (
    <div>
      <Switch>
        <Match when={(state.show)}>
          <Title title={(state.title)}/>
        </Match>
        <Match when={(!state.show)}>
          <div>switched off</div>
        </Match>
      </Switch>
      <button onclick={() => {
        console.log("switch on");
        setState({title: "test", show: true});
      }}>Switch on</button>
      <button onclick={() => {
        console.log("switch off");
        setState({title: null, show: false});
      }}>Switch off</button>
    </div>
  );
};

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

The two buttons are supposed to turn show on and off. However neither the <Match> elements update, nor does the createEffect code run when clicking "Switch off".

What is surprising: Replacing the <Title> call by a simple <div> not only renders correctly, but also fixes the createEffect behavior. Apparently the fact that there is a <Match> with a function call interferes with the effect system.

`Cannot set property 'textContent' of null` within JSX expression

I've been scratching my head about some rendering bugs I'm facing. When trying to come up with a minimal reproducing example of the actual issue (in a more complex scenario) I came up with something that has a different error/traceback, but might be related. I'm again not sure if I'm doing something wrong, or something is broken.

Example / Codepen:

export function View(props: { title: string }) {
  return (
    <div>
      <h1>{(props.title)}</h1>
      <table>
        <tr>
          <td><b>Title</b></td>
          <td>
            {(props.title)}
          </td>
        </tr>
      </table>
    </div>
  )
}

function Debug() {
  return (
    <div>
      <View title="test"/>
    </div>
  )
}

This errors in the JSX expression:

index.js:176 Uncaught TypeError: Cannot set property 'textContent' of null
    at insertExpression (index.js:176)
    at Object (index.js:195)
    at execToplevelComputation (solid.js:294)
    at createEffect (solid.js:36)
    at insert (index.js:195)
    at app_renderer.tsx:66
    at View (app_renderer.tsx:66)
    at app_renderer.tsx:84
    at Debug (app_renderer.tsx:84)
    at ./src/app_renderer.tsx.Object (app_renderer.tsx:93)

Maybe clarifying the problem of this simpler example helps to narrow down the problem in the full example.

Most likely the issue is in the babel-jsx plugin? Do you actually prefer tickets like these in this repo or in the babel-jsx repo when it's not fully clear if it's on solid side or babel-jsx?

Children as data?

To my knowledge, JSX allows to pass children as data in general. For instance, in React (to my knowledge) this code

return (
  <MyComponent>
    {1}
    {2}
    {3}
  </MyComponent>
);

would pass the array [1, 2, 3] into the children prop of MyComponent. Accordingly TypeScript seems to infer the type of children by finding the common base type of the children expression and wraps it into an array.

Currently, this disagrees with Solid's implementation of passing children, because it converts everything to a document fragment. This can be quite confusing, because according to the compiler the type of children must be an array, but it cannot be used as such, and it might be a gotcha for React users who are used to passing e.g. functions as children.

Bug: after re-create solid-component custom element, setState triggers unrelated computation node

I don't know this bug is in solid-js or solid-component so I just report in here.
This is my sample project: solid-js-bug.zip
So in my attached folder, in main.jsx, there is the code like this:

const componentMap = ['sub-element', 'div']
const App = (props, { element }) => {
   console.log('Create App')
   const [state, setState] = createState({
      selectedCompIndex: 0,
   });
   const ComponentSwicher = componentSwitcher();
   return (<div style="background-color: yellow">
      <p>App Component</p>
      <p>Selected Component Index: {(state.selectedCompIndex)}</p>
      <button onClick={() => setState({ selectedCompIndex: 0 })}>Select Component 0</button>
      <button onClick={() => setState({ selectedCompIndex: 1 })}>Select Component 1</button> <br />
      <ComponentSwicher id="component" _tag={() => componentMap[state.selectedCompIndex]} />
   </div>);
}

ComponentSwicher:

componentSwitcher = () => ({
   _tag: tagGetter = () => null,
   _is: builtInGetter = () => null,
   onInit = () => { },
   ...restProps,
}) => {
   let element;
   if (typeof tagGetter != 'function') tagGetter = ((value) => () => value)(tagGetter);
   if (typeof builtInGetter != 'function') builtInGetter = ((value) => () => value)(builtInGetter);
   if (tagGetter() == null) return null;
   r.wrap(() => {
      if (element == null)
         element = document.createElement(tagGetter(), { is: builtInGetter() })
      else {
         let newElement = document.createElement(tagGetter(), { is: builtInGetter() });
         element.parentNode.replaceChild(newElement, element);
         element = newElement;
      }
      r.spread(element, () => restProps);
      onInit(element);
   });
   return element;
}

The idea is that I can switch an element to another element, in this exemple, ComponentSwicher can change between sub-element and div.
This is the sub-element component:

const SubElement = (props, { element }) => {
   console.log('Create SubElement')
   const [state, setState] = createState({
      counter: 0,
   });
   return (<div style="background-color: rgb(200, 200, 200)">
      <p>SubElement Component</p>
      <button onClick={() => setState({ counter: state.counter + 1 })}>Increase counter</button>
      <widget-element counter={(state.counter)}
         onCounterChanged={e => setState({ counter: e.detail.counter })} />
   </div>);
}

And widget-element component:

const WidgetElement = (props, { element }) => {
   console.log('Create WidgetElement')
   let counter = props.counter; // deleting this line will bypass the bug, but I need to keep this line in my project
   return (<div style="background-color: rgb(255, 204, 153)">
      <p>WidgetElement Component</p>
      <p>Counter prop: {(props.counter)}</p>
      <button onClick={() => {
         element.dispatchEvent(new CustomEvent("counterchanged", {
            bubbles: true, composed: true, detail: { counter: props.counter + 1 }
         }));
      }}>Increase counter</button> <br/><br/>
   </div>);
}

If you click "Select Component 1", then click "Select Component 0", then click any of "Increase Counter" buttons, instead of increasing the counter, it just re-create the entire sub-element component (you can see my log in console).
If you remove this line "let counter = props.counter;" in widget-element, the bug will disappear.

Recommend Projects

  • React photo React

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

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.