S
SolidJS5mo ago
t

My first solid component

Just wrote my first component. I only wrote React before so I wanted to know if there are better ways to do things than what I figured out so far :)
import { createBreakpoints } from "@solid-primitives/media";
import { children, For, mergeProps } from "solid-js";

import type { JSX, ParentComponent } from "solid-js";

interface ColumnsProps extends JSX.HTMLAttributes<HTMLDivElement> {
columns?: number | Record<number, string>;
}

export const Columns: ParentComponent<ColumnsProps> = (props) => {
const merged = mergeProps({ columns: 3 }, props);

return (
<div {...merged}>
<For
each={getColumnArray({
children: children(() => merged.children).toArray(),
columnAmount:
typeof merged.columns === "object"
? Object.entries(createBreakpoints(merged.columns)).reduce(
(acc, [key, value]) => (value ? Number(key) : acc),
1,
)
: merged.columns,
})}
>
{(column) => <div>{column}</div>}
</For>
</div>
);
};

interface GetColumnArray {
children: JSX.Element[];
columnAmount: number;
}

const getColumnArray = ({ children, columnAmount }: GetColumnArray) =>
children.reduce(
(acc: JSX.Element[][], child, index) => (acc[index % columnAmount]?.push(child), acc),
Array.from({ length: columnAmount }, () => []),
);
import { createBreakpoints } from "@solid-primitives/media";
import { children, For, mergeProps } from "solid-js";

import type { JSX, ParentComponent } from "solid-js";

interface ColumnsProps extends JSX.HTMLAttributes<HTMLDivElement> {
columns?: number | Record<number, string>;
}

export const Columns: ParentComponent<ColumnsProps> = (props) => {
const merged = mergeProps({ columns: 3 }, props);

return (
<div {...merged}>
<For
each={getColumnArray({
children: children(() => merged.children).toArray(),
columnAmount:
typeof merged.columns === "object"
? Object.entries(createBreakpoints(merged.columns)).reduce(
(acc, [key, value]) => (value ? Number(key) : acc),
1,
)
: merged.columns,
})}
>
{(column) => <div>{column}</div>}
</For>
</div>
);
};

interface GetColumnArray {
children: JSX.Element[];
columnAmount: number;
}

const getColumnArray = ({ children, columnAmount }: GetColumnArray) =>
children.reduce(
(acc: JSX.Element[][], child, index) => (acc[index % columnAmount]?.push(child), acc),
Array.from({ length: columnAmount }, () => []),
);
No description
22 Replies
t
t5mo ago
Code and image are the same, I attached it because I find Discord's codeblocks hard to read
bigmistqke
bigmistqke5mo ago
1. What is the use of the .toArray() with the children? 2. children(...) returns a signal containing the resolved children. To get the actual children you would need to call it. 3. Something to look out for: props.children can either be a single element or an array of elements.
t
t5mo ago
1. that's the solution I found for iterating over children
No description
t
t5mo ago
No description
t
t5mo ago
merged.children is of type JSX.Element | undefined so I can't iterate over it props.children is of the same type so I think 2. is wrong
bigmistqke
bigmistqke5mo ago
interesting! that stackoverflow example will not be reactive but in your case it will work fine, since you are calling children() inside the JSX
bigmistqke
bigmistqke5mo ago
did not know about toArray!
No description
bigmistqke
bigmistqke5mo ago
but this would be reactive:
function Stuff(){
const cs = children(() => props.children).toArray
return <For each={cs()}>...</For>
}
function Stuff(){
const cs = children(() => props.children).toArray
return <For each={cs()}>...</For>
}
cool!
thetarnav
thetarnav5mo ago
calling children in jsx will be reactive but it will also wastefully recreate memos each time it reruns so better to go with what @bigmistqke wrote also it currently has an issue that if collumns prop changes, props.children will be executed again, recreating elements
t
t5mo ago
I think it's reactive already?
No description
t
t5mo ago
At least this works Unless I'm misunderstanding
t
t5mo ago
No description
t
t5mo ago
This seems to be working fine too even though I'm getting a warning @ line 22 This function should be passed to a tracked scope (like createEffect) or an event handler because it contains reactivity, or else changes will be ignored. I can't tell whether this is a false positive or something is actually wrong and I don't understand it @joshwilsonvu can you help?
bigmistqke
bigmistqke5mo ago
but it will also wastefully recreate memos each time it reruns
you are still creating a memo each time. better to bring children() outside of getColumns and into ColumnsProps body like
const Columns = (props) => {
const childs = childen(() => props.children).toArray;
const getColumns = () => childs.reduce(...)
const Columns = (props) => {
const childs = childen(() => props.children).toArray;
const getColumns = () => childs.reduce(...)
bigmistqke
bigmistqke5mo ago
This function should be passed to a tracked scope (like createEffect) or an event handler because it contains reactivity, or else changes will be ignored.
this might be solved by renaming getColumns to createColumns see https://github.com/solidjs-community/eslint-plugin-solid/issues/112#issuecomment-1844195070
GitHub
Enable customizing reactive rule to treat certain custom functions ...
Describe the need I am making a library for my team which handles data fetching with our custom protocol. The library will take in the arguments to the API call and return a signal, very similar to...
thetarnav
thetarnav5mo ago
generally if something starts with create or use or returns an accessor instead of the value, you probably want to use it at high as possible (eg in component body, parent component or global store), instead of inlining it in jsx, memos, or functions like getColumns. also even if stuff is updating correctly on the screen you might want to add random console logs to various places to see if something isn't updating more then it should in solid functions should rerun only when needed, no more no less eg elements are good to check if they are not recreated for some reason, because that might be expensive and cause weird behaviors later, even though it seems ok because "it's reactive" <div ref={console.log} /> <- will log every time an element is created
t
t5mo ago
Childs isn't getting called here so you can't reduce it That didn't work I don't think I understand how memos work How do I check if one is being created
bigmistqke
bigmistqke5mo ago
oops typo, u right. should be
const Columns = (props) => {
const childs = childen(() => props.children).toArray;
const getColumns = () => childs().reduce(...)
const Columns = (props) => {
const childs = childen(() => props.children).toArray;
const getColumns = () => childs().reduce(...)
it's mentioned in the docs https://www.solidjs.com/docs/latest/api#children
The return value is a memo evaluating to the resolved children, which updates whenever the children change.
with a memo we mean createMemo: https://www.solidjs.com/docs/latest/api#creatememo . you can think of it as a combination of a createEffect and createSignal: a signal that only updates whenever one of its dependencies change.
t
t5mo ago
Oh that makes sense As for this I think I'll just disable eslint for that line
bigmistqke
bigmistqke5mo ago
ye the linter is quite aggressive.. i never use it in my own code tbh can u post the code? wanna check it in the playground
t
t5mo ago
import { createBreakpoints } from "@solid-primitives/media";
import { children, For, mergeProps } from "solid-js";

import type { JSX, ParentComponent } from "solid-js";

interface ColumnsProps extends JSX.HTMLAttributes<HTMLDivElement> {
columns?: number | Record<number, string>;
}

export const Columns: ParentComponent<ColumnsProps> = (_props) => {
const props = mergeProps({ columns: 3 }, _props);

const columnAmount = () =>
typeof props.columns === "object"
? Object.entries(createBreakpoints(props.columns)).reduce((acc, [key, value]) => (value ? Number(key) : acc), 1)
: props.columns;

const resolved = children(() => props.children).toArray;

const createColumns = () =>
resolved().reduce(
// eslint-disable-next-line solid/reactivity
(acc: JSX.Element[][], child, index) => (acc[index % columnAmount()]?.push(child), acc),
Array.from({ length: columnAmount() }, () => []),
);

return (
<div ref={console.log} {...props}>
<For each={createColumns()}>{(column) => <div>{column}</div>}</For>
</div>
);
};
import { createBreakpoints } from "@solid-primitives/media";
import { children, For, mergeProps } from "solid-js";

import type { JSX, ParentComponent } from "solid-js";

interface ColumnsProps extends JSX.HTMLAttributes<HTMLDivElement> {
columns?: number | Record<number, string>;
}

export const Columns: ParentComponent<ColumnsProps> = (_props) => {
const props = mergeProps({ columns: 3 }, _props);

const columnAmount = () =>
typeof props.columns === "object"
? Object.entries(createBreakpoints(props.columns)).reduce((acc, [key, value]) => (value ? Number(key) : acc), 1)
: props.columns;

const resolved = children(() => props.children).toArray;

const createColumns = () =>
resolved().reduce(
// eslint-disable-next-line solid/reactivity
(acc: JSX.Element[][], child, index) => (acc[index % columnAmount()]?.push(child), acc),
Array.from({ length: columnAmount() }, () => []),
);

return (
<div ref={console.log} {...props}>
<For each={createColumns()}>{(column) => <div>{column}</div>}</For>
</div>
);
};
bigmistqke
bigmistqke5mo ago
thanks 🙏 i feel that the linter should be able to recognise these situations.
createEffect(() => {
resolved().reduce(
(acc: JSX.Element[][], child, index) => {
acc[index % columnAmount()]?.push(child);
return acc;
},
Array.from({ length: columnAmount() }, () => []),
);
});
createEffect(() => {
resolved().reduce(
(acc: JSX.Element[][], child, index) => {
acc[index % columnAmount()]?.push(child);
return acc;
},
Array.from({ length: columnAmount() }, () => []),
);
});
gives the same linter-error
Want results from more Discord servers?
Add your server
More Posts
How to keep store in sync with supabase's multitab session?A logged in user's data is stored in store: ```ts const initialState = { id: null, username:noob question regarding solid-routerHey guys, I'm running through this tutorial on Solid-JS as I am new to web dev, I got to the video oSolid-UI The `border-border` class does not exist.Recently got started with `solid-ui` and i love it a lot, however when i add it to projects where i How to Inform children of the active status of it's parent <A/>I'm looking at something like: ```ts <A href=".." class=".." activeClass=".."> {({ active }) => Solid-start + mdx: remark-shiki-twoslash does not workI tried the current version of solid-start with mdx and attempted to integrate remark-shiki-twoslashHow props.children get informed in router.jsx value change ? eg: logined =true,children refresh```jsx //app.jsx const App = (props) => { const [logined, setLogined] = createSignal(false); retError while using useNavigate() or <Navigate />PrivateRoute.tsx ` import { Route, useNavigate } from '@solidjs/router' import Index from '../pagesLowercase propsWhat is point of lowercase props "tabIndex" vs "tabindex", "onClick" vs "onclick" and etc?Good library for handling combobox-style search UI? (with Pagefind)I'm trying to create an Algolia-style interface with [Pagefind](https://pagefind.app)'s search API, Which Solidjs Router library should we useThere are different libraries for SolidJs Router in different documentations earlier it was " solid-