English 中文(简体)
SolidJs 动态环环环环环境不会在孩子面前负荷
原标题:SolidJs dynamic wrapping context does not load before its children
  • 时间:2024-07-22 01:36:38
  •  标签:
  • solid-js
community! This is my first StackOverflow question since I could find most of the answers so far, but this issue is very hard to find. I could see similar questions, but it does not resolve my intention and focusing on making the specific problem rather than answering the fundamental issue. ( I found a similar issue in the (Solid github)[https://github.com/solidjs/solid/discussions/1657], but does not look like it is reviewed by many people and only one answer says it is impossible) I want to fill up in the context once children elements are loaded. I have ParentComponent which will hold ContextProvider, and the context will pass import ./App.css ; import { createContext, useContext, splitProps, createSignal, For, children as Children } from solid-js ; export const UserContext = createContext(0); function App() { return ( <> ); } export default App; export const ParentComponent = props => { console.log( parent ); return ( props.children)()}> {(child, idx) => { console.log( child in for: + idx()); return {child}; }} ); }; export const Child = () => { const idx = useContext(UserContext); console.log( child , idx); return
I am child {idx}
; }; what I was expecting is seeing this. I am child 0 I am child 1 I am child 2 but what I actually see is this. I am child 3 I am child 3 I am child 3 I added console.log to see which one is loading first, and what I am seeing in the console is like this: parent child 3 child 3 child 3 child in for:0 child in for:1 child in for:2 As you see, after the parent is loaded, the children is loaded before the context provider logic. and it is never re-loaded. which makes passing information to the child impossible. Is there a way to control loading priority? Or is it a Solidjs s bug? as I expect this, even though the children is rendered first, once the context value is given, the children has to be rendered once more since the value of it is changed. But seems like it does not render again. I tried to make the idx to signal, but still have no luck. Thank you! I am trying to pass an extra values into children components not through props but through context. its value through the context is chosen based on its index. Whatever the value is given from the context to the children, children has to re-render to handle the newly given data from the context. ================ to avoid confusion, I will add more context about what I want to achieve. Below example has a complex dataflow. And I am expecting to pass the width 1,2,3 from the children in App component to be passed. But the result say it does not have existing props (width=3,2,1). import ./App.css ; import { createContext, useContext, createEffect, createSignal, For, children as Children } from solid-js ; export const UserContext = createContext(0); function App() { return ( <> ); } export default App; export const ParentComponent = props => { return ( props.children)()}> {(child, idx) => { // I made up below child.props. cannot find any way to pass existing props (width) return ( ); }} ); }; export const Child = props => { const idx = useContext(UserContext); return (
I am child {props.idx ?? no-value } with width {props.width ?? no-value } and idx from context {idx}
); }; result: I am child 0 with width no-value and idx from context 0 I am child 1 with width no-value and idx from context 1 I am child 2 with width no-value and idx from context 2 And if I use child from the children, then props and context value is not received. export const ParentComponent = props => { return ( props.children)()}> {(child, idx) => { const c = Children(() => child); // is there a way to pass existing child s props? return ( ); }} ); }; result: I am child no-value with width 3 and idx from context 0 I am child no-value with width 2 and idx from context 0 I am child no-value with width 1 and idx from context 0 What I NEED is both of given props from the children context or props given from the parent. In React, I can do it by using cloneElement, but in Solidjs, I cannot find similar functionality. Thank you for take a look into this issue.
最佳回答
I asked a question on the Solid github repo(https://github.com/solidjs/solid/discussions/2233), and got an answer about how to achieve this issue. Quick answer for the workaround is using this pattern: Child component just return props object instead of DOM element like null. Instead of trying to get some props from child "element", the child itself will be props or whatever object that is returned by Child. parent will have full access of children. using this information, we can render completely new children. the code will be like this: import { createContext, For, children as Children } from solid-js ; export const UserContext = createContext(0); function App() { return ( <> child1 child2 child3 ); } export default App; export const ParentComponent = props => { const c = Children(() => props.children).toArray(); const totalWidths = c.reduce((acc, child) => acc + child.width, 0); return ( {(child, idx) => { const width = (child.width * props.width) / totalWidths; return ( {child.children} ); }} ); }; export const Child = props => { return props; }; const ChildRender = ({ width, idx, children }) => { return (
Child {idx} width {width} - {children}
); }; and I can see an expected result Child 0 width 50 - child1 Child 1 width 30 - child2 Child 2 width 20 - child3 using context also works with this pattern: import ./App.css ; import { createContext, useContext, For, children as Children } from solid-js ; export const UserContext = createContext({ width: 0, idx: -1 }); function App() { return ( <> child1 child2 child3 ); } export default App; export const ParentComponent = props => { const c = Children(() => props.children).toArray(); const totalWidths = c.reduce((acc, child) => acc + child.width, 0); return ( {(child, idx) => { const width = (child.width * props.width) / totalWidths; return ( {child.children} ); }} ); }; export const Child = props => { return props; }; const ChildRender = ({ children }) => { const { width, idx } = useContext(UserContext); return (
Child {idx} width {width} - {children}
); };
问题回答
That is an expected behavior as Solid allows you to control execution order using component s structure + getter methods. In other words, it is up to you to execute children first or parent first, although it is a bit tricky to grasp the execution logic because the compiled code creates a closure and calls rendering methods that calls render effects which will be executed by the scheduler. Once I opened an issue on this very same topic, thinking it was a bug not to have a deterministic rendering order. You can read more about it: https://github.com/solidjs/solid/issues/1997 You can see this answer for how to solve it: https://stackoverflow.com/a/76995880/7134134 Context provider is just a regular component. Once you get how components are rendered, you can control its execution order. You can read more about Context API: https://stackoverflow.com/a/74492899/7134134 PS: OP marked his answer as the accepted answer in the provided answer. You need to read the linked answer. It is exactly the same issue. Context provider is executed first, that is why you get undefined value. Returning context provider and having it as a child component produce different results. I see your aim is setting the context value inside the For component but that forces you to return the provider component, which produces a different result. Correct way to provide a context value is keeping the receiver as its children: The problem with your code is that the execution order is reversed multiple times while the component tree is being executed, producing a result that is hard to reason about. Here is what your code produces: import { template as _$template } from "solid-js/web"; import { insert as _$insert } from "solid-js/web"; import { createComponent as _$createComponent } from "solid-js/web"; var _tmpl$ = /*#__PURE__*/_$template(`
I am child `); import { For, createContext, useContext } from solid-js ; import { render } from solid-js/web ; export const UserContext = createContext(0); const ParentComponent = props => { console.log( Parent ); return _$createComponent(For, { get each() { return props.children; }, children: (child, idx) => { console.log( child in for: + idx()); return _$createComponent(UserContext.Provider, { get value() { return idx(); }, children: child }); } }); }; const Child = () => { const idx = useContext(UserContext); console.log( child , idx); return (() => { var _el$ = _tmpl$(), _el$2 = _el$.firstChild; _$insert(_el$, idx, null); return _el$; })(); }; function App() { console.log( App ); return _$createComponent(UserContext.Provider, { value: 0, get children() { return _$createComponent(ParentComponent, { get children() { return [_$createComponent(Child, {}), _$createComponent(Child, {}), _$createComponent(Child, {})]; } }); } }); } render(() => _$createComponent(App, {}), document.body); Live Demo: https://playground.solidjs.com/anonymous/79dd68ec-79bc-45f7-9076-464fe74d45ae Here is how you can do it: import { type Component, For, type JSXElement, createContext, useContext } from solid-js ; import {render } from solid-js/web ; export const UserContext = createContext(0); const ParentComponent: Component<{ children: Array }> = props => { console.log( Parent ); return ( {(child, idx) => { console.log( child in for: + idx()); return child }} ); }; const Child: Component = () => { const idx = useContext(UserContext); console.log( child , idx); return
I am child {idx}
; }; function App() { console.log( App ); return ( ); } render(() => , document.body); Live Demo: https://playground.solidjs.com/anonymous/2197fa8f-fbe0-40f5-a18c-deb54280bb7e The compiled code for the above example is: import { template as _$template } from "solid-js/web"; import { insert as _$insert } from "solid-js/web"; import { createComponent as _$createComponent } from "solid-js/web"; var _tmpl$ = /*#__PURE__*/_$template(`
I am child `); import { For, createContext, useContext } from solid-js ; import { render } from solid-js/web ; export const UserContext = createContext(0); const ParentComponent = props => { console.log( Parent ); return _$createComponent(For, { get each() { return props.children; }, children: (child, idx) => { console.log( child in for: + idx()); return child; } }); }; const Child = () => { const idx = useContext(UserContext); console.log( child , idx); return (() => { var _el$ = _tmpl$(), _el$2 = _el$.firstChild; _$insert(_el$, idx, null); return _el$; })(); }; function App() { console.log( App ); return _$createComponent(ParentComponent, { get children() { return [_$createComponent(UserContext.Provider, { value: 1, get children() { return _$createComponent(Child, {}); } }), _$createComponent(UserContext.Provider, { value: 2, get children() { return _$createComponent(Child, {}); } }), _$createComponent(UserContext.Provider, { value: 3, get children() { return _$createComponent(Child, {}); } })]; } }); } render(() => _$createComponent(UserContext.Provider, { value: 0, get children() { return _$createComponent(App, {}); } }), document.body);




相关问题