Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

gbut posted:

I never even heard of it before this discussion, and it doesn’t even make that much sense. It’s a bit funny, yes, but if I was interviewing someone and they used that, I’d dock a point.
Same, except "dock a point" is a bit harsh, I'd probably have said "the what" and reproduced this discussion in the interview, thereby wasting some of their time.

Now that I know it's a thing, an interviewee could just do it and be fine.

Adbot
ADBOT LOVES YOU

gbut
Mar 28, 2008

😤I put the UN🇺🇳 in 🎊FUN🎉


roomforthetuna posted:

Same, except "dock a point" is a bit harsh, I'd probably have said "the what" and reproduced this discussion in the interview, thereby wasting some of their time.

Now that I know it's a thing, an interviewee could just do it and be fine.

I'm a softy. I actually wouldn't. I just wanted to be an internet tough-guy.

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
Possibly a really stupid question. I'm looking at React for the first time ever. There is a paginationControl component. When you press the component, it calls the left/right pagination function and adjusts the index by the size of the page.


JavaScript code:
const Paginator = (props) => {
  const pageSize = 10;
  const [leftIndex, setLeftIndex] = useState(0);
  const [rightIndex, setRightIndex] = useState(pageSize);

  const handlePaginateRight = () => {
    if (rightIndex > numberOfResults) return; // guard clause - although not necessary given button is disabled lower down

    console.log(`paginating right from:${leftIndex} ${rightIndex}`);
    setLeftIndex(leftIndex + pageSize);
    setRightIndex(rightIndex + pageSize);
    console.log(`paginating right to:${leftIndex} ${rightIndex}`);
    props.setPaginatedRecords(
      props.filteredRecords.slice(leftIndex + pageSize, rightIndex + pageSize) // This feels like a hack to me
    );
  };
As you can see, the left + right index are set, and then it should set the paginated records by slicing the filtered records at the appropriate point. However, it seems to use the original leftIndex/rightIndex which is why I've had to fix it with the slice leftIndex+pageSize, rightIndex + pageSize instead of what I assumed would worked originally (the state of leftIndex/rightIndex.

Both the console logs output the same thing, even though the second is after the states should have been updated. This is literally my first time looking at React (got to find myself a Udemy course soon) so I could missing something fundamental like 'the value from the state is static throughout the function call and would need to be done in a separate call' or 'it just hasn't updated fast enough' (tried to make it async and add awaits but that didn't seem to help).

Sad Panda fucked around with this message at 18:25 on Apr 3, 2023

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

Sad Panda posted:

Possibly a really stupid question. I'm looking at React for the first time ever. There is a paginationControl component. When you press the component, it calls the left/right pagination function and adjusts the index by the size of the page.


JavaScript code:
const Paginator = (props) => {
  const pageSize = 10;
  const [leftIndex, setLeftIndex] = useState(0);
  const [rightIndex, setRightIndex] = useState(pageSize);

  const handlePaginateRight = () => {
    if (rightIndex > numberOfResults) return; // guard clause - although not necessary given button is disabled lower down

    console.log(`paginating right from:${leftIndex} ${rightIndex}`);
    setLeftIndex(leftIndex + pageSize);
    setRightIndex(rightIndex + pageSize);
    console.log(`paginating right to:${leftIndex} ${rightIndex}`);
    props.setPaginatedRecords(
      props.filteredRecords.slice(leftIndex + pageSize, rightIndex + pageSize) // This feels like a hack to me
    );
  };
As you can see, the left + right index are set, and then it should set the paginated records by slicing the filtered records at the appropriate point. However, it seems to use the original leftIndex/rightIndex which is why I've had to fix it with the slice leftIndex+pageSize, rightIndex + pageSize instead of what I assumed would worked originally (the state of leftIndex/rightIndex.

Both the console logs output the same thing, even though the second is after the states should have been updated. This is literally my first time looking at React (got to find myself a Udemy course soon) so I could missing something fundamental like 'the value from the state is static throughout the function call and would need to be done in a separate call' or 'it just hasn't updated fast enough' (tried to make it async and add awaits but that didn't seem to help).

setting state isn't synchronous. never set state and then expect it to be your new value in the same method where you're updating state.

Your code is fine as is - most people should understand that it's using the old values - perhaps move the state updating to the end of the function so that's more clear that you're using the old values.

Or

don't call props.setPaginatedRecords but instead listen to state changes and call it inside a useEffect


TypeScript code:
import { useEffect } from 'react' 

const handlePaginateRight = () => {
    if (rightIndex > numberOfResults) return; // guard clause - although not necessary given button is disabled lower down

    setLeftIndex(leftIndex + pageSize);
    setRightIndex(rightIndex + pageSize);
};

useEffect(() => {
    /* 
        listen for when leftIndex or rightIndex changes and do something with the new values

        react also batches multiple state updates that happen in the same function so you can be sure this will only be invoked after both leftIndex and rightIndex are updated
    */
   props.setPaginatedRecords(leftIndex, rightIndex)
}, [rightIndex, leftIndex])
https://react.dev/reference/react/useEffect#useeffect

teen phone cutie fucked around with this message at 18:39 on Apr 3, 2023

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself
also here's a snippet from the docs that's describing your exact issue:

https://react.dev/reference/react/useState#ive-updated-the-state-but-logging-gives-me-the-old-value

prom candy
Dec 16, 2005

Only I may dance
I would avoid using useEffect if you don't need it. This is how I would refactor it:

TypeScript code:
const pageSize = 10; // You can take this out of the function body if it's just a hard-coded number

const Paginator = (props) => {
const [indexes, setIndexes] = useState({ left: 0, right: pageSize });

  const handlePaginateRight = () => {
    if (indexes.right > numberOfResults) return;
    const nextIndexes = { left: indexes.left + pageSize, right: indexes.right + pageSize };
    console.log('handlePaginateRight', { nextIndexes });
    setIndexes(nextIndexes)
    props.setPaginatedRecords(
      props.filteredRecords.slice(nextIndexes.left, nextIndexes.right) // This feels like a hack to me
    );
  };
}
This is typically how I handle a case where I need to update state and also immediately use the new value somewhere else. You could also store the indexes as a tuple instead of an object and then spread it in your call to filteredRecords.slice but that might be getting too clever.

TypeScript code:
const Paginator = (props) => {
  const [indexes, setIndexes] = useState([0, pageSize]);
  const [left, right] = indexes;

  const handlePaginateRight = () => {
    if (right > numberOfResults) return;
    const nextIndexes = [left + pageSize, right + pageSize };
    console.log('handlePaginateRight', { nextIndexes });
    setIndexes(nextIndexes)
    props.setPaginatedRecords(props.filteredRecords.slice(...nextIndexes));
  };
}

Soviet Space Dog
May 7, 2009
Unicum Space Dog
May 6, 2009

NOBODY WILL REALIZE MY POSTS ARE SHIT NOW THAT MY NAME IS PURPLE :smug:

Sad Panda posted:

Both the console logs output the same thing, even though the second is after the states should have been updated. This is literally my first time looking at React (got to find myself a Udemy course soon) so I could missing something fundamental like 'the value from the state is static throughout the function call and would need to be done in a separate call' or 'it just hasn't updated fast enough' (tried to make it async and add awaits but that didn't seem to help).

It is something fundamental, React's state variables are actually quite unintuitive if you are used to normal imperative programming.

If you look at this line of code you might think you are creating a leftIndex variable with a setter.
JavaScript code:
  const [leftIndex, setLeftIndex] = useState(0);
What is actually happening is you are creating a state variable (lets call it leftIndexState) that you should conceive of being "outside" your code. Whenever the page renders it creates a new leftIndex variable whose initial value is the value of leftIndexState. It also creates a setLeftIndex function that allows you to alter leftIndexState. What's unintuitive is that this happens after the code for a render is done, and during the render setLeftIndex has absolutely nothing to do with altering the value of leftIndex.

necrotic
Aug 2, 2005
I owe my brother big time for this!
It’s entirely intuitive if you understand the high level of how react works, and specifically called out in the useState docs that updates are for the _next render_.

I like to help people, but my first step is always “did you read the documentation for X?”

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
Thank you for all those really helpful comments. Going from synchronous imperative to asynchronous declarative programming is definitely going to be a fun jump. Got that bug fixed and a few more.

I've finished my JavaScript course on Udemy. It was useful to go over the fundaments of the language (and also how it works behind the scenes) but the projects I made were mainly 'here is the HTML, CSS and a flowchart for how the site should work now lets code it together'. I generally paused it to try to create it myself to avoid just watching someone type but I think my next step will be more something like theodinproject as its more 'here is a project go try it, you will definitely need to go read the docs'.

i vomit kittens
Apr 25, 2019


We've been rewriting a lot of our application to leverage asynchronous functions better because the amount of data our clients want to send us is much higher than we expected, causing our lambdas to both time out and run out of memory. Right now I'm working on fixing up a lambda that generates CSVs that was previously generating and appending each row of the CSV to a giant string sequentially. The first thing I did was switch to writing the rows to a stream, and also reading from the stream in parts to do a multi-part upload to S3 instead of a single put at the end. Now I'm trying to figure out the best way to handle the generation process.

The issue with simply doing it all concurrently is that the CSV output has to be ordered and grouped in a certain way. There are "categories" and "subcategories" to each row.
- The categories need to be ordered alphabetically.
- The subcategories within a category need to be grouped, but may appear in any order.
- Rows within a subcategory need to be grouped, but may appear in any order.

The solution I worked out goes something like:
JavaScript code:
const buildRowPromise = async (dbObj) => {
	const csvRow = await convertDbObj(dbObj);
	return csvRow;
};

const buildSubcategoryPromise = async (category, subcategory) => {
	const dbObjs = await getDBObj(category, subcategory);
	return dbObjs.map(async (dbObj) => await buildRowPromise(dbObj));
}

const buildCategoryPromise = async (category) => {
	const subcategories = await getSubcategories(category);
	return subcategories.map(async (subcategory) => await buildSubcategoryPromise(category, subcategory));
}

const handlePromises = async (categoryPromises, fileStream) => {
	while (categoryPromises.length > 0) {
		subcategoryPromises = await categoryPromises[0];
		while (subcategoryPromises.length > 0) {
			[rowPromises, subcategoryPromiseIndex] = await Promise.any(
				subcategoryPromises.map(addIndexToResolve)
			);
			while (rowPromises.length > 0) {
				[row, rowPromiseIndex] = await Promise.any(
					rowPromises.map(addIndexToResolve)
				);
				fileStream.write(row);
				rowPromises.splice(rowPromiseIndex, 1);
			}
			subcategoryPromises.splice(subcategoryPromiseIndex, 1);
		}
		categoryPromises.shift();
	}
}

const categoriesOrdered = await getCategoriesOrdered();
const categoryPromises = categoriesOrdered.map(async (category) => await buildCategoryPromise(category))
await handlePromises(categoryPromises, fileStream);
It seems to be working out in my unit tests so far but this is probably the most complicated async thing I've ever done so I'm worried that I'm missing something. Is there anything wrong with this, or some other approach I should be considering?

i vomit kittens fucked around with this message at 04:22 on Apr 12, 2023

cruft
Oct 25, 2007

I've got some custom code to fill values into elements, like

HTML code:
                <dl>
                    <dt>Participant Name</dt>
                    <dd data-fill="ticket.name"></dd>
                    
                    <dt>Ticket type</dt>
                    <dd data-fill="ticket.item"></dd>

                    <dt>Event Begin and End</dt>
                    <dd data-fill="hours"></dd>
                </dl>
You call fill(bigObjectOfStuff) and it fills all that in.

Is there a more standardy way to do this? Maybe some library I can use that doesn't bring in 50,000 dependencies? I don't mind rewriting the HTML, I just want something I don't have to document.

prom candy
Dec 16, 2005

Only I may dance

cruft posted:

I've got some custom code to fill values into elements, like

HTML code:
                <dl>
                    <dt>Participant Name</dt>
                    <dd data-fill="ticket.name"></dd>
                    
                    <dt>Ticket type</dt>
                    <dd data-fill="ticket.item"></dd>

                    <dt>Event Begin and End</dt>
                    <dd data-fill="hours"></dd>
                </dl>
You call fill(bigObjectOfStuff) and it fills all that in.

Is there a more standardy way to do this? Maybe some library I can use that doesn't bring in 50,000 dependencies? I don't mind rewriting the HTML, I just want something I don't have to document.

Do you have any concerns about people pulling info out of bigObjectOfStuff that they shouldn't be? I think if you're checking the attributes against some kind of whitelist you should be fine.

cruft
Oct 25, 2007

prom candy posted:

Do you have any concerns about people pulling info out of bigObjectOfStuff that they shouldn't be? I think if you're checking the attributes against some kind of whitelist you should be fine.

As soon as bigObjectOfStuff is sent to the browser it needs to be considered viewable by the user: I run cybersecurity training events :classiclol:

What I'm more interested in is whether I can use somebody else's library to do things like this. I'm not great at using third party code: right now I use Luxon and Bulma and everything else is written in-house. Surely there's some "client-side JavaScript fill values into the standards-compliant HTML" library?

prom candy
Dec 16, 2005

Only I may dance

cruft posted:

As soon as bigObjectOfStuff is sent to the browser it needs to be considered viewable by the user: I run cybersecurity training events :classiclol:

Oh right, duh.

quote:

What I'm more interested in is whether I can use somebody else's library to do things like this. I'm not great at using third party code: right now I use Luxon and Bulma and everything else is written in-house. Surely there's some "client-side JavaScript fill values into the standards-compliant HTML" library?

I don't know of any purpose-built libraries for just this. There are a lot of larger view libraries (React etc.) for doing this kind of thing but then you're going to be installing 50,000 dependencies. Maybe AlpineJS would cover you?

cruft
Oct 25, 2007

prom candy posted:

Oh right, duh.

I don't know of any purpose-built libraries for just this. There are a lot of larger view libraries (React etc.) for doing this kind of thing but then you're going to be installing 50,000 dependencies. Maybe AlpineJS would cover you?

That looks cool! But it's not what I wanted.

Maybe I should register a domain and create a slick web page for my fill function.

StumblyWumbly
Sep 12, 2007

Batmanticore!
Speaking of dependencies, my company has a AWS thing that was built up by consultants. I understand a fair amount of the internals, but I have never used JavaScript, my background is all C and Python. Now, when I try to make a completely unrelated change, AWS tries to build and comes back with some dependency errors.

Sorry if these questions sound ignorant, but I'd really appreciate any help. I am looking at the build output of 2 systems, the one from today that fails and one from September that succeeds. The build is a bunch of commands running through amplify.yml, I'm pretty sure I see what's going on but there could be something weird behind the scenes.
- When the build installs serverless (npm install -g serverless@">=2.72.2 <3.0.0") both the working and failing builds succeed but the working built prints out a big box saying "Serverless Framework successfully installed!" and some other info, and the failing one skips the box and goes straight to the other info. Is it possible these builds are using different versions of npm? The docker they're running from should not have changed, but it is controlled by AWS.
- Are there any general tools I should use to find and fix dependency issues?
- How bad is it that we're stuck on Serverless 2?
- It looks like this is all running from package.json. There is a yarn.lock file, but I don't see yarn getting used in the amplify build, just npm. Is this bad design? Package.json is full of stuff like "eslint": "6.x", which seems looser than it should be.

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

StumblyWumbly posted:

Speaking of dependencies, my company has a AWS thing that was built up by consultants. I understand a fair amount of the internals, but I have never used JavaScript, my background is all C and Python. Now, when I try to make a completely unrelated change, AWS tries to build and comes back with some dependency errors.

Sorry if these questions sound ignorant, but I'd really appreciate any help. I am looking at the build output of 2 systems, the one from today that fails and one from September that succeeds. The build is a bunch of commands running through amplify.yml, I'm pretty sure I see what's going on but there could be something weird behind the scenes.
- When the build installs serverless (npm install -g serverless@">=2.72.2 <3.0.0") both the working and failing builds succeed but the working built prints out a big box saying "Serverless Framework successfully installed!" and some other info, and the failing one skips the box and goes straight to the other info. Is it possible these builds are using different versions of npm? The docker they're running from should not have changed, but it is controlled by AWS.
- Are there any general tools I should use to find and fix dependency issues?
- How bad is it that we're stuck on Serverless 2?
- It looks like this is all running from package.json. There is a yarn.lock file, but I don't see yarn getting used in the amplify build, just npm. Is this bad design? Package.json is full of stuff like "eslint": "6.x", which seems looser than it should be.

if the automation is running the command "npm install" to install the dependencies in the package.json file, then yeah the yarn.lock file is there for nothing.

The point of a lock file is to ensure the same versions of dependencies are installed across machines. "yarn install" command will read from a yarn.lock file, while "npm install" will read from a "package-lock.json" file. if that "package-lock.json" file isn't there, that's most likely your issue.

yarn and npm are just two different dependency management CLIs. it sounds like your automation might need to be changed to run "yarn install" instead of "npm install"

fuf
Sep 12, 2004

haha
I've been having great fun with React and zustand making a little card-based game/app.

But I'm at the point now where if my list of cards gets long (around 200 or so) then the whole thing gets noticeably sluggish. So I'm entering a scary and confusing new world of performance optimisation.

I know that the first part is trying to prevent unnecessary re-renders, and I'm sort of getting to grips with that.

But there are also lots of times when my <CardList> component really does have to re-render its long list of <Card> components, and this is what's causing most of the slowdown.

Is there a way to wait for a component to render before displaying it? Or does that not make sense as a question? When I search for stuff to do with adding a "loading..." feature to components it's always to do with waiting for data to load rather than waiting for a bit of DOM to render.

I might also be misunderstanding the cause of the sluggishness. It might not be the actual rendering but something else going on (I'm sorting the list, and also using the "persist" zustand middleware to store it when it changes).

I'm trying to use the chrome Dev tools React profiler but it's pretty hard to interpret the results. Are there any other tools that might give some good insight into what's causing the slowdown?

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Why does your CardList need to re-render every single card? Is something about every single card being changed at the same time?

How long does it take to render a single card once? I wouldn't expect it to be an issue to re-render all 200 cards within a single frame - what might be an issue is if you're updating all 200 cards once each, and each update ends up re-rendering all 200 cards, resulting in 40,000 renders (less than 1% of which are actually required).

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Jabor posted:

How long does it take to render a single card once? I wouldn't expect it to be an issue to re-render all 200 cards within a single frame - what might be an issue is if you're updating all 200 cards once each, and each update ends up re-rendering all 200 cards, resulting in 40,000 renders (less than 1% of which are actually required).
You can get some sluggishness from re-rendering 200 things if each render is like "here's an image, and some canvas operations, and some text on a canvas". Even 200 simple blit operations onto a canvas can be a *little* sluggish.

One thing I'd suggest is, if you're rendering cards as text etc. give them a "cache canvas" - render to the canvas only if the card (or canvas size) actually changed, otherwise just blit from the canvas, or reposition the canvas (depending on the underlying structure you have). Doing this was about a 5x speed improvement on my mahjong game where the tiles were originally rendered as fairly simple vector graphics.

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

fuf posted:

I've been having great fun with React and zustand making a little card-based game/app

you can also do what twitter does (and a lot of other long list sites) and just render the stuff in the actual viewport with libraries like react window:

https://github.com/bvaughn/react-window

this will pop things in/out of the DOM as they're scrolled in and out. really good for performance with the only downside is you can't CTRL + F to search something out of the viewport

fuf
Sep 12, 2004

haha
Thanks for the replies.

Jabor posted:

Why does your CardList need to re-render every single card? Is something about every single card being changed at the same time?

The individual cards are not changing, but the array of cards is changing because it is being sorted, and cards are being added and removed, etc. I assumed that if the array changed then it would need to re-render every individual card, but maybe not? Can components change position within the DOM without technically being re-rendered if their internal content is identical?

The main action is moving cards between the "Deck" and the "Hand".

I don't actually have separate arrays for Deck and Hand, just one big "cards" array and individual cards have a property called "inHand". Then a <DeckList> that filters for !inHand cards, and a <HandList> that does the opposite. Maybe this is a fundamental design flaw.

If I run the profiler and move a card from Deck to Hand then the result looks like this:


It's hard to interpret the results because all the listed times for individual components are really low so it must just be a cumulative problem of having to render ~200 cards. Also I'm using react-beautiful-dnd to make the cards draggable so there's a long hierarchy of <Draggable>, <Droppable> etc. components (could this be part of the performance issue?).

If I go to my <Card> components then under "Why did this render?" it says "The parent component rendered".

The parent component for my <Card> components (through a few layers of react-beautiful-dnd stuff) is my <Deck> component, which uses my card list like this:

JavaScript code:
const cards = useDeckStore((state) => state.cards);
If I look at "Why did this render" for <Deck> then it says "Hook 3 changed". Hook 3 is the cards array in my zustand store:




I think I need to change it so that <Deck> doesn't re-render every <Card> whenever the cards array changes? But I'm not sure how.

In <Deck>, the list of cards is generated like this:

JavaScript code:
{cards.map((card) => (
   <Card id={card.id}>
)}
And then the top of <Card> looks like this:
JavaScript code:
export default function Card({ id}) {
  const card = useDeckStore(
    useCallback(
      (state) => state.cards.find((card) => card.id === id),
      [id]
    )
  );
Now that I post it I'm kind of embarrassed because I'm not sure why I'm doing it this way (with the useCallback). I think I must have read somewhere that this was better for performance to pass in the ID as a prop and then get the card (like it would only re-render if the id prop changes) but tbh I don't really know how or why it's working and whether this might actually be part of the problem.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
The profile sure looks like you've got 200 cards that are individually only moderately slow to render but combined take up a long time.

My understanding of react is that "rendering" a component doesn't actually do any slow DOM operations unless things have actually changed - I'm guessing that the source of your slowness is that the thing showing card 1 is being rerendered to show card 2, updating all the DOM elements with card 2's state; the component showing card 2 is being rerendered to show card 3; and so on down the line, when what you really want is to toss away card 1 and leave the rest of the DOM untouched.

I suspect you can do this by setting a key on your list so that react will match up before-and-after by card id rather than by list index.

necrotic
Aug 2, 2005
I owe my brother big time for this!
Yes, any time you render a list of items you should set a key attribute that specifically identifies that item (so not it’s index in the list).

https://react.dev/learn/rendering-lists

It should be logging an error message that you aren’t doing this.

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

necrotic posted:

Yes, any time you render a list of items you should set a key attribute that specifically identifies that item (so not it’s index in the list).

https://react.dev/learn/rendering-lists

It should be logging an error message that you aren’t doing this.

yep do this ^^^ and definitely set up a linter that will tell you you're missing it

JavaScript code:
{cards.map((card) => (
   <Card id={card.id} key={card.id}>
)}

fuf
Sep 12, 2004

haha
yeah sorry, I was trying to cut my posted code down to the basics to save confusion, but I do indeed have a key.

The actual code from <Deck> is:
JavaScript code:
.map((card, index) => (
                    <DraggableCard key={card.id} id={card.id} index={index} />
                  ))
and <DraggableCard> contains:
JavaScript code:
import { Box } from "@mui/material";
import React from "react";
import { Draggable } from "react-beautiful-dnd";
import Card from "./Card";

export default function DraggableCard({id, index}) {
  return (
    <Draggable key={id} draggableId={id} index={index}>
      {(provided) => (
        <Box
          ref={provided.innerRef}
          {...provided.draggableProps}
          {...provided.dragHandleProps}
        >
          <Card id={id}/>
        </Box>
      )}
    </Draggable>
  );
}
Sorry for the confusion, it would be great if that were the actual solution.

Combat Pretzel
Jun 23, 2004

No, seriously... what kurds?!
Today I lost a few hours trying to figure why my connection pooling class didn't work correctly. Turns out, that on arrays, splice() doesn't quite work like shift() and pop() and specifically needs a delete count, too. :suicide:

Didn't help that coding is my secondary activity and fell to the way side for a few months, because been busy with other things, and this being the first thing I'm back on (and last I remember, I ground my teeth on this already the last time when I stopped).

Roadie
Jun 30, 2013

fuf posted:

and <DraggableCard> contains:

The render prop there means it's probably fully re-rendering the contents every time, key or not, because it's a "new" child. This is part of why you see the render prop pattern much less in new libraries than back when it was the new cool thing everyone loved.

fuf
Sep 12, 2004

haha

Roadie posted:

The render prop there means it's probably fully re-rendering the contents every time, key or not, because it's a "new" child. This is part of why you see the render prop pattern much less in new libraries than back when it was the new cool thing everyone loved.

Removing the drag and drop functionality and wrapping the <Card> component in "memo" has helped a lot with performance.


I'm pretty sure I've pinned down the remaining sluggishness to the fact that I'm using the "persist" middleware for zustand, which saves the state to localstorage on every change. I'm currently saving the cards array to localstorage just because it's simple and I can keep the whole app self-contained without worrying (yet) about saving / loading data via an API. But now that the cards array is pretty big the constant saving on every single change to any card is causing issues I think.

I need to either remove the middleware and come up with my own save/load feature, or I need to change the "persist" middleware so it only saves periodically, or at least doesn't save on every change.

Combat Pretzel
Jun 23, 2004

No, seriously... what kurds?!
Opinions about Deno?

I've been googling random CommonJS vs. ESM things with what Electron and Node.JS and such, I suddenly find out about Deno. I guess nothing lasts long without a fork.

gbut
Mar 28, 2008

😤I put the UN🇺🇳 in 🎊FUN🎉


I wanted to try it as I like the idea of TS-"native" environment, but then the incompatible modules (and some other stuff I'm failing to remember now) made it hard to work for my use case and I gave up.
I might go back to it on a personal project in the future.

MrMoo
Sep 14, 2000

Both Deno and Bun sit in the land of being different but not bringing anything sufficiently better to the table.

If we could realize gains like uWebSockets does over “ws”, then that would make a change. That performance difference is insane, 10-30x faster.

MrMoo fucked around with this message at 17:22 on May 26, 2023

Vino
Aug 11, 2010
I don't really do js very often but I have a fun game side project in javascript and I decided that I miss not having types (I'm usually a C/C++/C#/etc type of programmer) so I decided to move the code to typescript and I've found some strange things I wanted to ask about. (And yes I realize a lot of this is actually javascript stuff that got adopted by typescript)

The syntax for importing modules works like this I think

code:
// a.ts
export class A { ... }

// b.ts
import * as A from "./a.js"
export class B { ... }

// c.ts
import * as B from "./b.js"
A.whatever // OK?
Because in c.ts import * from b.ts, it takes all symbols including the A that has already been imported into b.ts via that file's import line, so we get A in c.ts. Is that how it works?

If so, isn't this terrible? Not expected behavior at all and begs for cyclic dependencies. The way I'm working around it is by never using import * and by putting all my types in one big file called types.ts. What is the intended way of doing all this?

Constructors. Why can't there be multiple? Why must I define one and not use any kind of initializer syntax? It's a lot of writing to duplicate all the fields in the constructor. I would really like to be able to do something like

code:
class A {
   a: string = "";
   b: number = 0;
   c: string = "";
}

let a: A = A{
   b: 42,
   c: "aeou";
};
Is there any nice option like that?

Other than that Typescript is kinda neat actually, I really like how sum types are part of the language and not tacked on in a library like in C++.

prom candy
Dec 16, 2005

Only I may dance
Your read on how imports work is wrong. The only way you would bring A into scope is if you were re-exporting it from B, and even then you'd be accessing it as B.A (and similarly you'd be accessing class B as B.B)

Rather than import * I would usually encourage you to just import the things you want:

import { B } from 'b';

As far as the class stuff, I can't help you too much there. I (and lots of other JS/TS devs) don't use classes at all. It's not wrong to use them but lots of people prefer not to.

necrotic
Aug 2, 2005
I owe my brother big time for this!
If you just need a data bag, define A as an interface and then you can just assign an object of that shape.

code:
interface A {
  foo: string;
}

const thing: A = { foo: “value” };
Phone posting excuse the fancy quotes.

If you need methods on your thing then a class is better, obviously. Yoy can’t have multiple constructors because JavaScript doesn’t have that concept. A single constructor can be define in typescript with overloaded type definitions but the actual implementation must then handle all of those manually.

edit: Typescript _does_ allow you to declare fields _in the constructor_ so you only have to specify once. Can use public, protected or private as the access level and that declares it as a field on the class itself, without the need to manually assign or declare the field separately.

code:
class A {
  constructor(
    public foo: string,
  ) { }
}

const a: A = new A('bar');
console.info(a.foo);

necrotic fucked around with this message at 20:01 on Jun 21, 2023

Obfuscation
Jan 1, 2008
Good luck to you, I know you believe in hell
As for that latter example, normally you don't use a class in Typescript just to hold data like that, you are using a javascript object and a typescript interface to add types to the contents of that object. Since TS uses duck typing, you don't need to declare an explicit type for your objects, as long as they fulfill the expected interface they are ok.

Not sure if I'm making sense but here's a code example to show what I mean:
TypeScript code:
interface Foo {
	a: string;
}

const doSomethingWithFoo = (foo: Foo): void => console.log(foo.a);

const someData = { a: 'hello' };
doSomethingWithFoo(someData); // this is totally okay
edit: beaten, but at least I have fancy colors

Obfuscation fucked around with this message at 20:12 on Jun 21, 2023

Vino
Aug 11, 2010
Very helpful, thank you!

prom candy
Dec 16, 2005

Only I may dance
You can also define types from objects, which can be handy.

code:
const fooThing = {
   a: "",
   b: 0,
   c: "",
}

type Foo = typeof fooThing

// This won't compile
const barThing: Foo = {
  a: 1,
  b: "hi"
}

Vino
Aug 11, 2010
No way! I love that

edit: Seems like a design miss on js part that it doesn't run the constructor in that case, but I never put anything important in constructors anyway.

edit 2: It is a bummer that it requires you to specify every class member when the class has defaults in it that it could easily use.

Vino fucked around with this message at 19:18 on Jun 23, 2023

Adbot
ADBOT LOVES YOU

necrotic
Aug 2, 2005
I owe my brother big time for this!
There’s no constructor for Plain JavaScript Objects, they are just a map!

JavaScript actually doesn’t even have actual classes! The class keyword is syntactic sugar around the prototype system.

A naked { foo: “value” } is an instance of the very root “type” of Object, it’s not creating a new “thing”.

The type definition example above is all typescript magic, not JavaScript at all.

You _can_ use its "defaults", though!

TypeScript code:
const fooThing = {
   a: "",
   b: 0,
   c: "",
}

type Foo = typeof fooThing

const barThing: Foo = {
  ...fooThing, // "Copy" the object into this one
  a: "farts", // assign a new value
}
Note that this spread operation only copies primitives! Any arrays or more complex objects are copied by _reference_. Example:

TypeScript code:
const fooThing = {
   a: [1, 2, 3],
   b: {
     foo: 'bar',
   },
};

type Foo = typeof fooThing

const barThing: Foo = {
  ...fooThing,
  a: [5, 6, 7], // Here we _replace_ a with a new array
};

barThing.a.push(8);
console.info(fooThing.a); // Still 1, 2, 3!
console.info(barThing.a); // 5, 6, 7, 8

barThing.b.foo = 'baz';
console.info(fooThing.b.foo); // Oops, this is now also 'baz'!

necrotic fucked around with this message at 19:35 on Jun 23, 2023

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply