Help Me Understand Documentation Holes #167
Comments
|
I think having a formal API generated from the codebase would be very helpful; sometimes it's hard to find which methods are available, what are they called or what types of values are accepted (I'm thinking about all the possible way to call setState). Given it's a typescript codebase, https://typedoc.org could be helpful, even if it's not the most flexible generator (it looks something like this on a project of mine: https://framp.me/frappe/docs) My background is mainly React and derivates (from Classes to Fn/Hooks) + a fair amount of elm + very little vue.
I probably would have described the system starting from reactivity and introducing the simpler primitive and then building up to state. |
|
I know there's an open issue about SSR & SSG, but a more detailed documentation about it and how to set it up would be really nice. I think that's going to spark a lot of interest, especially if you manage to get it close to Marko/Svelte rendering. |
|
@framp this is great stuff. The state naming is an interesting thing in the post Hooks world. See I started calling that beforehand because I was trying to emulate React Class Components state object. But now people are used to
Yes I need to document create solid better.. It is just a fork of Create React App. I just pointed at CRA docs because that all applies but it's worth talking about that. For instance adding module.css as the extension I think does CSS modules out of the box(https://create-react-app.dev/docs/adding-a-css-modules-stylesheet/). The problem is there is so much in CRA that I don't know how it works. Of course I should just make that a lot clearer. Thank you so much for this feedback. @amoutonbrady .... hah.. you got me. This is purposefully cryptic right now since I'm making breaking changes even in patch versions. I shouldn't be but I don't want to bump the minor version to infinity while I work on this. I know how to get Marko/Svelte performance but it comes at real cost given the scenarios I want to support. I almost have to start over and build up again to get there. Basically they render a static string with no reactivity on the server this makes advanced patterns very limited. But there are different sorts of solutions once you accept that. I'm still profiling the cost of reactivity. So far seems I can get Vue like performance, but have to make a call whether that is good enough. |
|
I can't really talk about what the experience of someone with no experience in other frontend frameworks would be, since I'm not that person, but as someone who has mostly only worked with React as well, coming to Solid when I see things that are named "Effect" or "State", I would really want to know either exactly how they differ to React effects and state, or if they are substantially different, it would be nice if they were named something else entirely. I think the problem with the current explanation of Solid State in the README is that it isn't 100% clear exactly how effects or state work in Solid. For example, React hooks execute every single render, or if you add state variables into the dependency array, only when those state variables get updated. In Solid's README if you read the provided example code one might be able to deduce that the effect only executes if state variables used inside are set through their setters, but it would be good if this was stated explicitly in the explanation. Also, it still leaves a few questions unanswered, like "so does the effect ever execute if state variables inside are not set?", and "if one uses a specific path inside the state object, does the effect only execute if that path is updated? Or just whenever the setter is called?". Whichever is the case, a more explicit explanation would be nice. You also don't explain:
The two linked introductory articles are a bit better, but explain by showing some amount of the implementation, which can be insightful, especially in this case where the underlying implementation isn't hugely complicated, but can also be confusing to some people. Generally speaking as well, while talking about implementation detail can be fine if it is explained clearly why they make things work they way they do, it introduces one more thing that a new user needs to understand, and is only useful if it helps them understand how to use the library, or helps them understand why the library is good/superior. I also understand if the introduction on State is merely meant to motivate, not explain, but in that case I would expect a more thorough explanation on both State and Effect somewhere else in the official documentation (and not an article written on another site). And also, if the goal was to motivate, personally speaking, I don't see from the provided examples how the library is all that different to React, and if I was a beginner it wouldn't really occur to me why Reactivity is even that important in the first place. It's only through digging through other parts of documentation and the two articles that any of it clicks into place. The Reactivity.md document starts by talking about Signals, but I hesitate to actually read about them, because 1. I'm not sure whether it's actually necessary to understand them, and 2. the actual explanation for what signals actually are seems to mostly be a very heavy two paragraph section titled "Accessors Reactive Scope" which should really have more code examples to demonstrate what you mean by things like "reactive scope" and "can be nested as many levels as desired". If signals are just implementation detail then I don't really want to read about them. |
|
@louisch Thank you this cuts to the heart of it. I've had difficulty explaining the reactive update model without going into implementation. Any wordy explanation sounds like vague generalizations or like I'm selling magic. This always detracts from having a simple message. I do see a couple commonalities here:
|
|
@ryansolid I think you should include this example shared state between multiple components also how to use createEffect without running into issues |
|
@junaid1460 did you remove another example? I actually liked that one. Since it showed something that I took for granted. Computations re-track dependencies on every execution. If you ever hit branching logic that doesn't depend on a reactive stat the other branch will never evaluate and the branch itself won't since it isn't being tracked. I think the execution cycle of reactivity tracking could be better explained. As for the CodeSandbox example has a lot going on so I'm unclear what precisely you are trying to demonstrate. Generally I recommend following React-like patterns even if it is unnecessary. I find that code organization easier to understand the basics like It is possible to hoist out signals and state, but you don't want to do so with effects and memos since they will never be released. For that reason I strongly suggest following the Context pattern. I have examples about how that works and docs. But maybe I need a more of.. writing your first app etc.. Or like after a writing your first Component, a next steps. I'm thinking this mostly boils down to having good tutorials. The examples are ok, the docs definitely have holes, but as much of it is they go too deep in areas a beginner doesn't care to as of yet. I need the documentation to be complete but there should be a path for people who don't care for those details. I introduced state early to let people coming from React try easy stuff almost as a drop in. |
|
Yeah, kinda changed it. with making two components almost similar except for one line. I was trying to explain how tracking works. May be you could include a simple example in a best practice doc Also there's some other pattern like passing JSX element directly in props might cause duplication like for example function Test({array, index}:{array: any[], index: number }) {
return array[index]
}
function Root({index}: {index) {
// Avoid this
<Test array={[<MyBigComponent></MyBigComponent>, <i></i>]} index={index} />
}
function Test2({array, index}:{array: any[], index: number }) {
return array[index]()
}
function Root({index}: {index) {
// do this
<Test2 array={[() => <MyBigComponent></MyBigComponent>,() => <i></i>]} index={index} />
}This looks very minor issue, but this might cause mem leak while using external libraries like monaco or Pickr, also too many subscriptions to state if element uses state. I think a good dev tool for chrome can notice these issues and notify. May be I'll try to build one. |
|
Is the confusion that all dynamic props are lazily evaluated? And: <MyBigComponent />
// roughly equals
MyBigComponent()Because I don't see an issue in the first case if that is what you are going for. Can you explain why don't do this? I also notice you use a lot of destructuring which shouldn't be a problem here but could lead to missing reactive triggering. I'm thinking that something even more fundamental is missing in the docs and following that assumption we've gotten out here somewhere. Maybe there is a bug here that I'm missing as I expect the first scenario to be fine. Maybe my heuristic for dynamic wrapping is incorrect in these component cases? I feel like I'm missing part of what is trying be achieved here. Using |
|
I have migrated a project from react to solid, I noticed this with a tab component, during change, updates were triggered in two createEffects, out of which one is visible and another was just invoked by this |
|
Haven't tried solid yet, but read a few articles by @ryansolid & documentation pages in this repo. Solid looks like a very promising project and I really want to see it gaining more traction among developers. With that, I'd strongly recommend the project to have its official website with documentation before/with going v1.0. If you're already planning on that, please disregard the rest of this comment. As silly as it sounds, presentation matters. A lot. A nice, clean website to go with the documentation will attract new users and help to retain current users. This is especially true for those who are not as knowledgeable/interested in the underlying technologies but still curious about what the next big thing might be or simply looking for some great off-the-shelf tools to build applications with. A link to the project website also looks much better than to its GitHub repo when spreading the word. Unfortunately, https://github.com/ryansolid/solid just doesn't give off an impression that this is more than an experiment. Nothing too crazy--something as simple as https://recoiljs.org/ should suffice in my opinion. You already have all the materials. Best of luck to you! |
|
@bobaekang I agree completely. It is already in the works. I hope to get it in place soon. It is probably the biggest outstanding piece for 1.0. I wouldn't mind stabilizing on SSR a bit more and a bit of cleanup around Resource APIs for Suspense. I think starter templates could be improved too. I appreciate the feedback. |
@ryansolid could you please drop a link to repo here. I'd like to take that up. |
|
Right now it's a private repo. I will likely change when I have something to show. |
|
Can you elaborate on Does |
|
Just started looking at Solid (having found it via interest in Svelte). I'm most familiar these days with React (and Redux), but have used a bunch of approaches/tools over the years (including good ol' VanillaJS). Solid looks very interesting, especially as I've hit some performance bumps recently with React in a side project. I know I can work around them, but I keep having this feeling that browers and JS are incredibly powerful these days and we are actually getting in our own way with a lot of our 'solutions'. So Solid's focus on being fast resonates a lot! Regarding docs, some general points that would help folks like me jump in are:
Right now there is a ton of information that I've read through about Solid that's got me excited about it. But I also notice that I haven't yet tried playing with it because I dread having to make a bunch of decisions (even which 'starter' to use). Reducing those decisions by providing a strong opinion would help a lot here. I looked over the realworld and hackernews source. I think it would be amazing to document the decisions made (and accompanying rationale) when implementing those - could be the basis of the tutorial? Are they recommended patterns for Solid usage? By the way, the general explanation of why (along with the what and how) in the current docs/articles is very useful and something often overlooked in docs. Distilling these down into a summary of the tradeoffs made could be helpful to folks evaluating potential fit for their projects. Lastly, a tie-in with an existing service can be a boon to adoption and visibility. I saw #100 and think that getting an article on their learning section could be a good idea. They are also a good example of real-world articles that get you into their product by showing how it works with other common products (like Auth0). |
|
Really like the idea of having a tutorial/tour page of Solid's features. |
|
@martinpengellyphillips Yeah I did try to streamline at least getting started. The challenge is everyone has their own opinion. I have Create Solid App. And truthfully the fact that I wouldn't use my own starter probably says something. In general, I've found the process on tooling has generally been I suggest something and everyone points out how they have a different use case. This is a problem with the JS ecosystem in general. There is no one size fits all here. For larger projects people have more patience for bloat. I find most early adopters would rather start pretty minimalist so something like Create Solid is way overkill. But it also isn't there for them. I pictured most people would just start there as documented. I tried to write an article for how to build TodoMVC but I feel it fell short. The real challenge is answering "why" without getting into very specific details of the reactive system. From my perspective most libraries gloss over this a bit. They just go, this is how you do blank which I think is probably sensible from an intro perspective if why makes the explanation 4 times longer. Personally I think the single best way to try SolidJS without any commitment is modifying one of the over a dozen examples from CodeSandbox I link off the readme. That's how I learn. That's the poor man's version of tutorials. Which really just brings us back to the tutorial/guides which keep coming back in these comments. I'd love to be able to do something like that. But it starts with getting a REPL setup. Something nice that shows code running, as well as compilation output. That is pretty much the requirement to make those tutorials effective. I've had Solid compile in the browser before using Babel Standalone but this needs to be much better. With that I think the rest of this comes. I'm not sure if there are existing editor tools that can be used? I know Svelte built theirs from scratch. I could make an issue around creating a REPL. It's just one of many things that will take some doing. As much as I'd like to do stuff like this to promote the library. I've resigned myself to prioritize things that only I can do at this point and things that others could help with like say Routing, creating a Website, or creating a REPL I'm going to leave out there for now. If anyone wants to help would be super appreciative. Otherwise I will get there eventually. |
|
@ryansolid I hear ya!
I personally don't think you need a REPL tutorial (it's a nice to have). What I was thinking more about was some clearer sense of "Ryan's sitting down to start a new project with Solid - what do they do?". And I think that, as the creator, you totally get to give your opinion without worrying about satisfying everyone elses And maybe you don't use starter templates and just spin things up from scratch. If so, I'd say that in the intro/tutorial because it helps establish the culture ('lean and mean', 'back to basics', 'understand your tools', ...) and it also avoids folks spinning the wheels too much on 'am I doing this as intended?'
I totally clocked that and it was a stumbling block for me getting started because I wanted to know why doesn't Ryan use this? Can I do what they do instead?.
I think that's a good way to try bits of Solid, but for me it's not a hook to use Solid. I need to build something with it and see how the pieces fit together. And that means I need low friction on getting started on my own project. As an aside, CodeSandbox is typically slow so not a great compliment to a project that focuses so much on performance BUT, to stay true to my earlier point, if you think lots of examples via sandboxes is the way to go, then just stating that clearly will help folks get in the groove. In the meantime, I'll take a punt at converting one of my existing apps to Solid and let you know how it goes. |
|
I'm not sure if I've missed this, but one topic that may be worth covering is how to generally approach the integration of 3rd party vanillaJS libraries with Solid (e.g. libraries for animations, drag-and-drop, etc.). This is a topic that has been in the back of my mind while thinking about (and very much wanting) to use solid for an upcoming personal project. I don't know if I've overlooked or just failed to understand something, but there could be other dumdums like me that are also unclear as to what to consider and how to approach trying to use existing vanillaJS libraries with Solid. |
|
That's fair. I think I assume too much React knowledge and don't provide any guides for this as it is a bit outside of what the library does. In one sense it's really simple, which is why it gets missed I think since: // this is an HTMLDivElement
const div = <div />In that it is pretty easy to do Vanilla JS since you just get a reference to the element and do what you would with it. You can also get refs via the ref bindings https://github.com/ryansolid/solid/blob/master/documentation/rendering.md#refs. If you need to cleanup when your component is destroyed you can add an Albeit I don't have great examples. This thread might be of interest: #39 I'd be interested in more concrete examples as a source for guides. |
|
As kind of the culmination of the long chain of questions I had in #215, I think this line: in the Reactivity docs could use a bit more elaboration. Reactivity in Effect and Memo are very clear, I've been basing my understanding of reactivity off of S, so those read to me just as S computations... But it's a lot more opaque when it comes to JSX, I think (Which is an issue I think I also have with Surplus). I originally interpreted this as meaning of "an expression in [the] JSX" as "a JSX expression" as in "All JSX is compiled to a createEffect call", which lead to the assumption that anything inside the body of a Component function would therefore be reactive, but from the conversation we had and the article you linked, I understand that's not the case. I'm assuming now that "an expression in JSX" is what the curly brace interpolation thing is called, and I guess I just never picked up on that. So, does this mean that any function that's ultimately invoked within JSX bracket whatevers is reactive? Does this apply universally, like in props and such? I think it would be useful for people who are prone to making way too many assumptions, such as myself, to explicitly lay out where the boundaries of reactivity are. EDIT: I think I might just be parroting what louisch said above. Also, when you say <Component someProp={getSignal()}/>Is not reactive, unless I were to use |
|
@Myrdden Yeah it's all good. It just means they can be better. I made a lot of changes thanks to @louisch comments. But you bring up such an obvious thing yet not obvious to me since I am in it all the time. Those curly brace things are called JSX Expressions in the spec and the AST parsed. I actually named the underly DOM Expressions after this concept but it is in no way obvious since in JSX from a JavaScript perspective everything is an expression. To answer your first question yes. Component execution bodies themselves are untracked, but every expression which can be reactive inside a JSX Expression is reactive. The compiler uses syntax analysis to determine this. Things that call functions or access members (like array or object property access) are treated like they are reactive. Function declarations, literal values, and simple identifiers that can never be tracked are not (it's a little bit more complicated than that but that's the message). Second question. With components the compiler instead of wrapping each prop in Still I've struggled to explain this. Lazy evaluation is necessary so that access happens inside a reactive context as desired. But I understand it isn't clear what that means since I sort of hide it behind property access. |
|
Neat, that totally cleared it up for me. My takeaway is basically that the assumption can be made that anything inside of Effect, Memo, or a JSX Expression is reactive, but I think that did a good job explaining why. One more question, how exactly does destructuring work? Is it just "never use it with reactive things"? I understand that Actually, while we're at it, it might be useful to have a list of potential pitfalls documented somewhere... |
|
Honestly I think this is the by far the biggest one. It's all variations on this topic of reactive context and reactive property access and tracking. All reactive libraries fundamentally work the same but since I don't do this at a Component scope it causes a lot of confusion. Unfortunately it is the thing that makes this approach interesting from performance standpoint. Why it is able to do the 1:1 updates that no other library even Reactive ones like Svelte can do. It's much easier to just say never destructure. But the truth is for something to be reactively tracked we need to call a function. We can hide that function behind a getter or a proxy but we need to call a function while the reactive context is executing. So nothing is wrong with destructring as long as you are in the right place. Destructuring is accessing properties on an object and assigning them to a value. A value can not be reactive. So it matters where you access that property, so a destructure tracks like any other reactive access. But if you do it at the top of your file no one is listening. I looked at Vue 3 docs for suggestions of how to explain this as they have the same problem in their setup function. But they just point to a helper that sort of destructures into a bunch of refs(signals). But it's verbose and expensive. I guess I could write simple function wrappers with explicit keys but it stops looking like destructuring. It becomes sort of self-defeating when the goal is to keep things terse. I could do it with a proxy maybe and keep the syntax tight. It wouldn't work in a rest parameter. Like: const {someProp} = toSignals(props);
// now someProp is a function and reactive when called.
someProp()The request has come up a couple times now. Any thoughts on something like that? A bit more ergonomic perhaps than We do have some other helpers like |
|
Personally I'm happy with "never destructure reactive props or else they'll break". I usually tend to use use |
|
Hello, another question... Maybe this goes in it's own ticket? Not sure. I recall you saying somewhere, I don't remember where, that const Component = () => <p>hello</p>;
const App = () => <div><Component/></div>;
// versus
const App = () => <div>{Component()}</div>;
// or even something like
const App = () => wrapDiv(Component());will these all function exactly the same? Or is there some other logic to EDIT: Forgot that I, as a programmer, posses the ability to look at other people's code. So I'm seeing createComponent when I'd assume the answer to this is yes, but I figured I'd be thorough and ask. |
|
Yeah I might even remove // consider:
<Component prop1="static" prop2={state.dynamic} />
// becomes:
sample(() => Component({
prop1: "static",
get prop2() { return state.dynamic }
}))Does that makes sense? Generally, ignore the memo it's for a special case in which the component returns a function. But I'm intending to solve that a different way in the future to avoid unnecessary wrapping. |
|
@jeff-hykin Yeah it's super tricky with JavaScript. It goes beyond the templating since you can't intercept just a value read. Which either sticks you on Solid/Vue path or Svelte (which is locked to the template). There is research being done to get around that but it is complicated. Generally speaking I think being explicit as much reasonably possible is probably the solution. One thing that I push with Solid is to always call the function on the signal and not just treat it like Solid early days used a syntax to denote reactive values. It took me a while to find something that wouldn't break TypeScript but unfortunately Prettier killed it pretty badly. I used inner params I'm hoping too documentation can help. I've been wrestling with this stuff for about 5 years now and gone through many permutations but reasonably happy where I am now. But stuff like destructuring is probably always going to be a problem until we can do cross module analysis through compilation. And basically invent our own reactive language like Svelte that goes beyond the components. I might do that one day. |
|
Oh that's really interesting, the {()} and {$()} are similar to syntax I was considering. But I think my approach might be a bit different than expected. My thought was having a The template would be able to identify the special syntax (e.g. On the flip side, if a reactive value was accidentally used in the template (e.g. With babel-macros the template work can be done at compile time, but I think templated work could also be inefficiently done at runtime just like other methods in Solid. |
|
@jeff-hykin Compile time reactivity with compiling in the dependencies is one of the first things we tried for the next version of Marko(I also work on eBay's JavaScript framework). There are a few performance cliffs and we ultimately went more Svelte-like. The short of it was we were taking on a lot of the overhead of runtime reactivity and not getting all the benefits. It is infinitely more challenging admittedly to get cross-module reactivity with that but we've developed an approach. The idea of fail-dangerous is interesting to me. See in the past fail-dangerous in a reactive system meant triggering crazy ping ponging of updates. Basically over-rendering was a problem. Now that we've done everything possible to prevent that we have the issue of losing updates. I do agree that though that proxy style hiding of reactivity does cause this to go undetected where as a special symbol or even signals (whose special syntax is The only place we can really shave for more performance at this point is making fewer subscriptions and short of some compile away reactivity system that works cross-module anyone would be hard-pressed to find better performance. It would be possible to do a 1 for 1 proxy with say the DOM to do better, but that approach wouldn't scale to shared data or scheduling updates. So given the focus on performance, I like the set of tradeoffs made here. Re-reading the comments though a lot of good thoughts. Especially around async. We've been looking at that a lot. The funny thing is while on paper a lot of those limitations seem bad. In practice it's hard to even come up with reasonable expectations of how they should behave. Like if you had an async function why would you want to track after it returns. Would that update cause it to start all over again? Or from that point etc.. I think you could make it do something but it actually doesn't fit the mental model anyway. I guess it's still a gap to get to the point where it is obvious that if you have a condition in a tracking function it too has to be trackable so that the different branches can execute. When done this gives more granular reactivity than any compiled solution can do. I think that has been the challenge. Even when explained the implications are a bit of a slow trickle. But I think your list is a good starting point and I will use that. |
|
I'm not sure what cross-module reactivity means, but I'm glad to hear some form of compile time tracking and fail dangerous have been explored. In terms of a feature, would manual/explicit effects for a function be considered bloat, or could it potentially be part of Solid.js if I worked on it as a PR? Seems like it could allow for slightly less overhead (no initial function call, and effect events don't need to be triggered with each call), and could maybe be used internally within the Solid codebase. The sleek black magic "it just works... well at least most of the time" is fine for projects with tight teams that can explain the caveats and pitfalls, but both @aminya and I (@aminya leading the way) see Solid.js as the best framework for the Atom Editor. And with the motto of "the hackable editor", it kind of needs to be designed in a way that doesn't require intimate knowledge of a reactivity system that has many caveats. So yes, this method of explicit requirements definitely would not be syntactically sleek, but I think it would make it more maintainable with a lower skill requirement. Technically I guess it's possible as-is. Just seems like a bit of a runtime waste with the extra function calls. |
|
I'm a bit confused with the comments on async, so maybe I'm missing the point with the following comments. If tracking/effect system was perfect then I'd say async itself already has the correct behavior except for the final level; the Dom. For example, lets say you had a reactive variable The only thing is the final output, like the DOM needs to await the promise before changing. And with JSX that would probably require a placeholder element with display:none until the first execution has finished. Now... That's all if implementation wasn't an issue. Given the current effect system, I have no idea how to practically keep track of effects while awaiting values. |
|
One of the issues I have had is that by default, the child components are not reactive when a parent changes the props that are passed to that child. The workarounds I have learned through experience are the following. Using these two workarounds I have been able to use
export function Child(props) {
const [getSomeProp, setSomeProp] = createSignal(props.some_prop, false) // pass false to createSignal
// update the local copy whenever the parent updates
createComputed(() => {
setSomeProp(props.some_prop)
})
}In addition to better documentation, we probably need some Eslint rules that help us write correct Solid code. I have been using the recommended preset of eslint-plugin-react with |
There is an What's weird about that example is why the condition isn't a signal. That's one of the strengths of this sort of granularity. When the condition is false we don't even subscribe to count. If you update it every 100ms, no extra work is being done when the condition is false. Static dependencies don't solve that which is one of the challenges I've hit for compiled reactivity.
createEffect(() => el.setAttribute("title", state.maybePromise))So it naive approach would need to be createEffect(() => {
const value = state.maybePromise
if ("then" in value) value.then(v => el.setAttribute("title", v))
else el.setAttribute("title", value)
})Obviously could wrap in a helper especially since I'm missing promise cancellation here.
We are still missing each other. You really don't want components to be reactive. Ever... I think I'm missing some scenario or something you are trying to do. Is it 2-way binding? Resetable forms? Like why a local copy of state/signal to just wrap a prop when you could use the prop. Or a derived value. The problem is if possible you more than a single source of truth there will either be synchronization or indirection on read. Indirection on read might actually be a good reusable pattern as it could avoid creating additional computations. But it sounds like right now you are in a place where you are trying to work around the system and I'd like to understand why. |
|
Not sure if relevant, but for context I've used the pattern of local state/signal which is updated when parent passed prop changes. The two use cases I had were forms (passing in new initial values post submit, whilst still allowing the form to edit values locally until submission) and drag and drop (local copy of ordering to avoid updates interfering with an in progress drag). I use createEffect for updating the local copy when passed prop value changes. |
For me, the components are a unit of code-reusability. A component allows me to make a package like solid-simple-table, and others only need to use it in their components and pass data into it. When a component is used inside another component, the default assumption should be reactivity from the parent. I am assuming that Solid cannot perform multi-component analysis to detect this, and that's why we can't fix this automatically. If I had a // static props:
// destructure the props that are not tracked and are used inside the loop
const {
headerRenderer = defaultHeaderRenderer,
bodyRenderer = defaultBodyRenderer,
getRowID = defaultGetRowID,
accessors,
} = propsreact-table switched to hook-based package in version 7, but that approach doesn't make sense to me (it is much more difficult to use than a simple component). We shouldn't need to make everything (even the package) a hook. |
|
@martinpengellyphillips Thanks for the example. In the past (where I didn't have state at my disposal) I did similar synchronization. Generally not using a computation, but manually on the submit action or as an exposed API, but the same idea. Mostly that in my case I couldn't always guarantee that the value from above would change. Like if you are just resetting the props from the parent might not change. This pattern of exposing an API isn't an obvious pattern and I haven't seen it as much as of late. If you pass in an object as a prop the consumer can write APIs to it. I suppose another approach is always to deep clone the starting data on reset and pass it in. That would guarantee updates.
By fix you mean simulate top-down rendering? Even with perfect analysis that wouldn't be the direction here. Props coming in are reactive and the child can react to them. I don't understand where the idea the child isn't reactive coming from. As best as I can tell the |
|
The problem is to make a component that when the parent changes the props passed to it, it updates the rendering. For example, if the parent adds a new entry in the data, the table adds a new row.
Here is the code of the parent: Here is the code of the child (which is supposed to be a reusable component): If you would like to play with the example: By false/true I mean changing the notify option in By |
|
(Coincidentally getting back on the topic of improving documentation) I think there's a point of confusion here @aminya @ryansolid. If I'm understanding correctly, for a single instance of a component, the render function is only ever called once, and then Dom is mutated only by using effects right? So one part of @aminya's code basically has function TableComponent(props) {
return <table>
<For each={props.columns}>
{(column) => /*stuff*/}
</For>
</table>
}Since |
|
@ryansolid fyi, for my form example it's not actually a reset as such. It's a bit special to my needs, but on submit the parent handles formatting the data to a nicer display version. And then I just reuse the form for display. It's an unusual live edit form and display situation. But this is actually what I like about solid. I end up building just what I need. I'm not tempted to try and find a formik library and then make it do what I want. That said, is there a gotcha with my approach here that I'm unaware of? I guess I could pass in a copy from parent that is then directly mutable...though I recall some issue with merging state like that |
|
@aminya @jeff-hykin My new tutorials show this off a bit but you also see this in most of list examples like TodoMVC. Essentially the main reason things aren't updating is because you are setting the same array. const rows = getRows()
rows.push({ file: "New file", message: "New message", severity: "info" })
setRows(rows) // still the same array... solid has no knowledge anything has changedNow you can always notify by turning the equality check off like you have been doing. But you basically have to do that everywhere. Generally the approach is: setRows([...getRows(), { file: "New file", message: "New message", severity: "info" }] ) // different arrayNow you won't need those The reason for the computed is that you are accessing the value in an untracked context. // This initializes a new signal with the current value of props.row... no dependencies are made
const [getRows, setRows] = createSignal(props.rows, false)
createComputed(() => { setRows(props.rows) })There are many ways to do things here so I can't point you at a specific solution. But once you choose to make new local state or signals you need to keep it in sync with a computation. But you could just use it directly like @jeff-hykin example. You could use If synchronization is desired, the thing I'm thinking is if it would be possible to make a primitive to handle this clone reset scenario, but I don't think it would be generic for all cases. Like if it were simply signals (no state).. I could picture a sort of writable clone... function createClone(fn) {
const [s, set] = createSignal();
createComputed(() => set(fn());
return [s, set];
}
// use like
const [getRows, setRows] = createClone(() => props.rows);I don't know just spitballing. Many options here. And missing pieces. Like how do changes get back up to the parent. Etc.. Usually a single source of truth is better. But I acknowledge that sometimes better for the child to handle sort of temporary(editing) state rather than just have it reflect back to the parent immediately. @martinpengellyphillips |
Oh, I see. Is this the same for Can't we add a counter inside the set functions that detects the numbers that a function is called and if it is incremented, then it means a new diff check is required? If we could, then we can trust the user that is calling |
|
Yes but createState is nested so you can opt to do a nested update. Like you could just set the index at the end of the array to effectively push on the new item without cloning. In general we do not diff data. In fact diffing the signal would make no sense since the only thing that could notify is the signal itself and we can't account for all the different downstream ways it could be used. Like say we diff we know it has changed, but the downstream just gets the data and doesn't know what has changed and would need to diff again. So we did that diffing for basically no value because presumably who ever is doing the setting could make that determination. If we wanted to always notify we could that is what the Basically, the defaults make a big difference here, and when consider that effectful changes can have real side effects (we run cleanup every time they re-evaluate) restricting notification is important. An equality check in the computation is too late it needs to happen at the point that the upstream signal decides whether to notify. So when trying to decide between guarding by default and not I landed on guarding because it is the easiest the explain and it basically removes the opt in aspect of this. When designing slightly more complicated things we'd be pretty aggressive guarding and then have to instruct the end user that we guard like this so pass in unique data. Now it's just universal. Basically well written re-usable libraries for performance reasons will be guarding for most things. And as people started writing more libraries it became harder to hide that detail or deal with the inconsistency. I sometimes opt into notify always but it's in local scopes. Like triggering something. For external facing APIs I feel notify on change is the only way to be consistent. It does occur to me though that the result is a little strong-armed even if it is a clear message. But Vue and MobX are the same defaults. Sure you don't experience this as much because they rely a lot on the proxies, like Solid's State object. So maybe that is just the solution here. Their state is mutable though so I bet people don't even realize it. But generally don't grab reactive data and mutate outside of the reactive system and expect it to update. It's possible in dev mode that I should be returning readonly proxies that warn people trying to mutate this data. |
Dang |
|
To be clear if you are using a Vue proxy it isn't what I mean by outside the system. Vue's reactivity isn't doing any sort of deep diffing. It is very similar to Solid's. I default to readonly proxies but there are the ability to make mutable ones. By outside the reactive system I mean taking non-wrapped values and updating them and expecting them to change. For instance holding reference to the initial value being passed into a reactive initializer and updating it. In other cases I mean shallow reactive values. Vue I believe wraps deep by default so you probably never see this. But Solid SIgnals are shallow by default. If you are looking for Vue like behavior use |
|
I'm still definitely struggling with knowing when a component will update (and especially what will fail to trigger an update). The differences between |
|
@jcuenod I'm hoping the tutorials address that pretty well. I think we need more interactive examples to appreciate it since even explaining it doesn't really make sense when I say shallow versus nested tracking and what that means. You can give them a try here: https://dev.solidjs.com/tutorial/introduction_basics. There are some bugs in terms of navigation and loading race conditions but maybe this will help. (If it doesn't load refresh) |
|
@ryansolid I hadn't seen https://dev.solidjs.com yet. It looks great! Yes, I have noticed some loading bugs but (other than that) I like it. I think you need to explain "tracking scopes" (at least, when I encountered the term, I didn't know what it means in practice, even though I can guess its definition). |
|
Just saw this issue. Referencing #504 |
|
Does Solidjs support Generators in the event lifecycle? |
|
Good call on the interopt. Bringing other libraries into Solid is pretty easy but the opposite matters completely on the mechanisms of the host framework. For client side its mostly just a ref and As for generators no. The lifecycles are a bit fake anyway. Its all just reactive subscriptions. Tracking in generators doesn't really make sense to me and unclear what we'd be yielding to. I guess the settling of the reactive system. Not sure. Probably worth some thought. I wonder if introducing them core would create potential issues for concurrent rendering. |
|
@ryansolid I don't know if you looked into the effection library, which uses generators to have Structured Concurrency, but it got me excited about using generators to solve some issues I've been having. Composing concurrent microtasks seems pretty natural with generators. It also performs well, better than async await & is memory efficient over large iterators. |
|
Stuff when I started:
|
|
@btakita I imagine this would be a larger change. MobX has things like @pfgithub Did by chance you see the tutorials on https://dev.solidjs.com? I was hoping they would supplement the docs sufficiently. |
|
@ryansolid That makes sense. Thank you for the explanation. I am running into wanting to add an interface to the |
|
I agree we collecting some stuff in the Discord. I'm just uncomfortable writing it myself. I do not feel confident in my knowledge of TypeScript at all. Some people have contributed some very powerful typing to Solid but I feel after almost 3 years on TypeScript it still hasn't clicked for me. |
|
@ryansolid Just wanted to say that video yesterday was excellent for explaining the reactivity system. I think the interview format made all the difference, making the video an order of magnitude better than the current reactivity guide linked in the readme.md Linking that video in the documentation under something like "how does Solid know what to update?" would fill the biggest documentation hole for me (and would fully address my original message on this thread) |



I really would appreciate feedback here. It is so hard for me to see it since I've been so close to this for so long and I've developed apps in this manner for 10 years. I'm going to be the worst person to write the documentation since it is all so second nature for me. Yet, I am most likely going to be the person writing the documentation since I understand how everything works so well.
So can you please comment on anythings you found confusing about Solid as you've been trying it out, using it in your demo projects, integrating it with 3rd party libraries. And if you did find a solution can you comment on what you feel would have been the easiest way to describe it to you. Even if it takes me some time to compile all of this I think it would also highlight some early gotcha's for people trying the library.
I know I need to do a lot better here but this is a bit of a blindspot for me. Thank you.
The text was updated successfully, but these errors were encountered: