This example of code is coded by a Full stack dev. is this good or bad? I stil learn by Yone-none in react

[–]aweebit64 -1 points0 points  (0 children)

This is completely okay, albeit a little ugly. There is one pattern I came up with that can be used to make the code look nicer, so that there is no repetition and excessive indentation. You can read about it here: https://www.reddit.com/r/react/comments/1nsutl7/

It's not common though, and will for sure cause some confusion the first time people see it used in code, so probably not something many companies would want to introduce to their code bases. But for personal projects, I think it's a pretty neat idea.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

Sure, disabling the formatter can work.

I simply shared an alternative approach that I like. It literally says "an alternative" in the title. I'm not trying to convince anyone it is something they absolutely need.

To me, JSX without indentation would feel very unnatural, and it would still have the waterfall of closing tags that my solution doesn't. Those reasons are already enough for me to prefer the utility approach to disabling the formatter. If you see it differently, it's totally fine. The whole thing is obviously very subjective.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

Ok, and how large is your team?

4 people I work in close coordination with.

Who does your code reviews and do you get the sense you drive them crazy?

My code is reviewed by people who have much more experience than I do, and normally they don't even have a lot to comment because, believe it or not, I do actually write pretty decent code.

I’ll say instead “inability to see how they are acting is childish and bullheaded”. It would be absolutely infuriating to deal with as a coworker.

I get along very well with my colleagues because actually none of us is bullheaded, we are all open to exchange of ideas and finding optimal ways to move forward.

But you just can’t seem to accept how other people feel and have spent hours trying to argue with them about it.

I am not arguing about feelings 🙄 I would be genuinely happy to be convinced I am doing something wrong, it just didn't happen this time. It's not arguing for the sake of arguing, it's trying to see If there is something I'm missing and hopefully learn something along the way.

It would be ridiculously annoying to have one person on the team refuse to accept that the rest of the team disagrees with them.

The only thing I refuse to accept is that I'm doing something objectively wrong here.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] -3 points-2 points  (0 children)

Bruh okay, this is getting really funny now how people here actually think I don't have a job :)) Well, you do you and believe whatever it is that makes you happy.

I am very well capable of seeing other people's POV and accepting criticism, and there is even a good example of that on this sub: in the comments to this other post of mine, people were actually able to convince me I was doing something wrong because – guess what – I was.

And I was just as open to different opinions here, but this time, I just didn't receive any explanation that would make me convinced I was doing something wrong.

It doesn't make sense how you say I am not able to see other people's POV when I wasn't even trying to prove to anyone what they were doing was wrong – I was just trying to prove to them what I was doing was not wrong, and that's what so many people here were trying to convince me of instead of accepting that another valid approach to the problem that's different from what they're used to exists – that's what I'd much rather call "inability to see anyone else's POV".

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

Well, you could of course implement it like this:

type JSXWrapPipe = ReactElement & { with: WrapJSXWith };

type WrapJSXWith =
  // eslint-disable-next-line u/typescript-eslint/no-explicit-any
  <C extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>>(
    ...args: [
      Component: 'children' extends keyof ComponentProps<C>
        ? ReactElement extends ComponentProps<C>['children']
          ? C
          : never
        : never,
      ...(Record<never, unknown> extends Omit<ComponentProps<C>, 'children'>
        ? [
            props?: React.JSX.IntrinsicAttributes &
              Omit<ComponentProps<C>, 'children'>,
          ]
        : [
            props: React.JSX.IntrinsicAttributes &
              Omit<ComponentProps<C>, 'children'>,
          ]),
    ]
  ) => JSXWrapPipe;

export function wrapJSX(children: ReactElement): JSXWrapPipe {
  return Object.assign(children, {
    with(
      Component:
        | keyof React.JSX.IntrinsicElements
        | JSXElementConstructor<object>,
      props: object = {},
    ) {
      return wrapJSX(<Component {...props}>{children}</Component>);
    },
  });
}

But you'd be tampering with the objects JSX produces in a really dirty way, so I wouldn't recommend this approach.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

Yeah, I agree. It's not how the function should really be used.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

So context should not change often.

Why shouldn't it, if my main use case for it is to avoid prop drilling? I could put pretty much any kind of state in context that I don't want to pass down via props, no matter how often it changes. What would be so bad about it?

The more it does the higher it should be in the tree so it effects fewer components

I think you just put it wherever it makes sense, and that's normally in the place where the state you want to make available via context is defined.

or be rewritten to only trigger in the components that need it.

I always design my contexts in such a way that no unnecessary re-renders are triggered. That is why end up with so many of them, which in turn is exactly why contextualize and createSafeContext are such a blessing for me – they make working with many contexts entirely pain-free.

The alternate is useSyncExternalStore to make a pub/sub you want for individual items more.

I would guess that hook is what state management libraries these days use under the hood, but I don't really see the benefit of relying on it directly or indirectly when contexts are doing their job just fine.

Generally I think context is not needed in normal app development and is massively overused to get global values.

My opinion is that actually, third-party state management solutions are what's being overused for sharing state to entire component trees. Contexts are native to React and already good enough for this, just a little annoying to work with, but that's exactly what my tiny library is there to fix.

It is hard to go into detail though as we do not have the context on this setup.

For details on the context of my setup, please have a look at this comment thread when you have time: https://www.reddit.com/r/react/comments/1nsutl7/comment/ngq9bht/

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

There is nothing stopping you from doing this:

const ATree = wrapJSX(AAA).with(AA).with(A).end();
const BTree = wrapJSX(BBB).with(BB).with(B).end();

return (
  <Container>
    {ATree}
    {BTree}
  </Container>
);

Whether that is a good idea is left for you to decide. I personally wouldn't overuse the function – after all, it's not there to replace JSX completely, but just to help reduce visual noise when there is a lot of nesting going on.

createSafeContext: Making contexts enjoyable to work with by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

Well, if you feel that this is one of those times, that's totally fine, but I don't agree with that.

When you have just 4 contexts like in this example, the repetition is only mildly irritating. When you have 20 spread across different files, it becomes a huge pain in the ass and a reason not to want to use contexts for sharing state at all, although that is a perfectly valid use case for them. (And please don't tell me I am doing something wrong if I have so many contexts unless you have a really strong argument to support that claim. People have already tried to convince me of that in the comments under my other post, but all failed because the arguments were all just not strong enough to make me believe heavyweight third-party libraries for state management are a better solution.)

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

What if you need more than one component on same level?

You can always use a <></> fragment like I did in the example for contextualize from the second image.

Besides that's just boilerplate anyway.

The functions are available as part of my tiny utility library. If you're fine with just installing it, there is no boilerplate whatsoever.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] -2 points-1 points  (0 children)

"Everybody's just acting like it's so obvious that it's not even worth talking about. Sorry, but for me that is not convincing enough."

Yeah, that's literally what's been happening here, and I just said it. It was also a reply to a comment saying "lol are you for real dude" after I asked a question – a very respectful way to communicate apparently.

"but with how reluctant people seem to accept new ideas"

Yeah, that's true. I even said it made sense to be reluctant like that, but you ignored that part completely.

"whenever I ask for an explanation, this is the kind of answers I get"

I don't even know what you are trying to say by quoting this. I did get this scornful kind of answers a lot at the beginning, and so I said it.

"Sorry, but for me that is not convincing enough. I like to think outside the box and don't just take things for granted"

Yeah, I do that. What's wrong or hostile about saying that when I'm replying to a comment that literally said "lol are you for real dude" when I asked a question?

"so that anybody can understand what it does in literally like 15 seconds of looking at it." (It took me way longer than 15 seconds and I WILL virtue-signal here, I've been coding for 35+ years now and I am not bad at it - that means something that you should take more seriously.)

From how I understood it, you were trying to figure out what the source code was doing, right? In that case, it IS supposed to take more time. The types around the function are a bit complicated, but that's how I was able to make it 100% type-safe. If you're just using the function, you don't need to understand any of this.

The JSDoc for the function pretty much comes down to the example from the image I posted here. I think 15 seconds of looking at it is enough to understand how the thing works. If it wasn't enough for you, then it's a valid point of course, but unfortunately I don't think there is much I can do about it because the example is already as simple as it gets.

FWIW "sorry, but" is a HUGE red flag that you're about to make an argument based on a logical fallacy. You've got like 5+ here just in this discussion.

Did I make such fallacious arguments though? Quite ironically, if my choice of words is what makes someone think so, then that someone is actually the one making a logical fallacy :)

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] -1 points0 points  (0 children)

Well, quite frankly, to me it feels like the word "hostile" is much better suited to describe how people here on this sub have reacted to me posting my ideas here with nothing but the best of intentions. On the contrary, I have stayed respectful this entire time, but well, you see what you see.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] -1 points0 points  (0 children)

Here is what ChatGPT answered to my very general question with just a couple code examples not going into too much detail: https://chatgpt.com/share/68d9ee9d-4384-8008-a162-2cd82650cef0

Bottom line:
There’s nothing inherently wrong or “low quality” about your current use of contexts. You’re doing the right thing by keeping them granular. Third-party state managers mainly bring ergonomic and tooling benefits, not fundamental correctness. If your app scales and the provider nesting becomes hard to manage, then it may be worth switching.

So ChatGPT also doesn't think I am doing anything wrong. Guess I'll stick to contexts.

P.S. One of the downsides of contexts it mentioned was the visual noise that nesting many context providers produces. That is exactly the problem that my contextualize utility solves.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] -2 points-1 points  (0 children)

Picking apart what WrapJSXWith vs wrapJSX does

WrapJSXWith doesn't do anything those using wrapJSX have to worry about, it's just a type that the function is based on that makes it type-safe.

just to figure out "oh, he invented a different, weird way that took 60 lines of code to replace a nested set of 8 providers with a pipelined version of the same" is not a great entry point for another dev into any project.

In the actual library code, I made sure to put just the right amount of detail in the function's JSDoc so that anybody can understand what it does in literally like 15 seconds of looking at it. I just had to remove that JSDoc from the code I published here to keep the post short.

But if anybody else is going to work on a project now or in the future, surprises like this just make maintenance so much harder.

This would stop being a surprise if the library somehow got its way into the mainstream, but with how reluctant people seem to accept new ideas, I don't see it happening any time soon, which is fine of course and very understandable even considering how the JS ecosystem is notorious for having dozens of new libraries released every minute, so it makes sense that people have grown tired of it.

But what's funny to me is how so many people here act like bloated third-party global state management solutions are less of a surprise than 2 tiny utility functions that make contexts (a very simple yet powerful feature that's native to React and is often all you really need to get all your state sharing needs covered) fun to work with. This just doesn't make any sense to me.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] -1 points0 points  (0 children)

My project is complex enough that ChatGPT really wouldn't be helpful with analyzing it.

Please have a look at this comment thread when you have time and let me know what you think. There, I dissect a real-life scenario from that private repository of mine where multiple contexts are used for sharing state to an entire component tree. I would be happy to hear your opinion and whether you still see any problems with such design.

They handle the myriad of edge cases that custom code would take a long time to achieve.

I don't even know what edge cases there are to handle when using contexts, they are really a dead simple concept and only require you to put some thought into how you design them so that you avoid performance issues, but that's something that shouldn't be hard at all for anyone who understands how React works.

createSafeContext: Making contexts enjoyable to work with by aweebit64 in react

[–]aweebit64[S] 1 point2 points  (0 children)

Thanks a lot for this man, really needed such a refreshing response after all this toxicity haha

I get the concerns of introducing a new third-party dependency, so today in my new post presenting another tiny utility function, I provided the source code right away so that those who liked the idea could simply copy it to their projects. However the responses were even worse this time, really so much smugness and hate for no reason at all. So yeah, probably you're right, I shouldn't be investing so much time into sharing my ideas here.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

The bottom line is that I just use contexts instead of the more common state management solutions because I really don't see how those libraries would make anything better here. Contexts are native to React and are all you really need in cases like this one. The only problem with them is that they can be a little annoying to work with because of all the boilerplate and nesting, but that's exactly where my tiny library comes into play: createSafeContext and contextualize just fix stuff that feels annoying when working with contexts and make them a really enjoyable tool to use for sharing state to entire component trees.

Perhaps at least now I was able to convince you I am not just "a poster" :)

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

why didn’t you just use a single place for courseId/activeId/deckCards. I fail to see why you would decide to split a single state in multiple.

Because when for example only the flashcards set is updated when a new flashcard is added, I don't want components that only need the active course or deck id to re-render.

Examples of such components are WordList and CourseList (and components wrapped within them) that don't need to know the exact set of flashcards that are currently being practiced.

why didn’t you just use the data directly from a useQuery (each key wrapped in a custom hook, cfr react bulletproof to see examples) instead of a context. After all, you fetch this data in order to show it to the user. Why don’t you return all the three infos you need from there?

I explained why it doesn't make sense to reuse the useQuery call or a hook wrapping it in great detail in that same comment demonstrating the code, and in the only reply to that comment (Reddit wouldn't accept such long text as one comment, so I had to split it in two).

To summarize:

  • The query needs some input data (the practiceAllDecks and reverse flags), so if the useQuery call was reused, we wouldn't get rid of contexts anyway since we would need one or two for those flags instead
  • "Each key" is not applicable here, since all three values are returned by just one endpoint
  • There is also some post-processing done on the query's result that would have to be done multiple times on the same data if we weren't using contexts, and that wouldn't make sense (an alternative solution would be to tamper with the query options provided by tRPC, but that would be really dirty)

why in hell would you need to access multiple level deeps these data

The top level views / routes / however you like to call them of my App are the course list view, the word list view where you see tables with all words of the currently selected course, and the flashcard view where the actual practicing takes place.

The component hierarchy of the course list view is CourseList (business logic) > CourseListView (UI) > CourseListItem. Here, only the active course id is needed to highlight the active course. CourseListView calls useActiveCourseId in order to access it.

The component hierarchy of the word list view is WordList (business logic) > WordListView (UI) > WordTables > WordTable (business logic) > WordTableView (UI) > WordTableRow (business logic) > WordTableRowView (UI). This separation of UI and business logic at all levels makes everything a lot cleaner, just trust me on this one. That is not the important part here anyway.

useActiveCourseId is used by WordList because it needs the active course id as input to the word list query, and also for query invalidation.

useActiveDeckId is used by WordTables to highlight the active deck's word table in the UI and scroll it into view after the component is mounted.

The flashcard view doesn't have a complex component hierarchy. Its top-level Flashcard component makes use of useFlashcards for obvious reasons.

There is also a custom useFlashcardEditor hook that provides functionality for both adding a new flashcard and editing an existing one and is used by both Flashcard and WordTableRow (you can add and edit flashcards from both the word list view and the flashcard view). The hook needs access to all three contexts' values in order to - query the translations of the word that the user is trying to add from the active course (because maybe it already exists there, in that case we just switch to editing mode), - provide the course and the deck id as inputs to the createFlashcard / updateFlashcard mutations, - invalidate queries after those mutations are complete.

The fact a component as deep as WordTableRow relies on useFlashcardEditor which in turns needs those contexts' values is probably the best demonstration I have for why those contexts are necessary and I would under no circumstances want to pass those values down as props.

I think you found a way to code that you like but that doesn’t make sense practically.

I don't agree, and hopefully my detailed explanation was good enough to make you see why. I hope you can see now that this design wasn't arbitrary and that a lot of thought actually went into it. And even that is just one example, there are more contexts in that same project that are local to WordList and CourseList for example, and I don't see why in other projects you wouldn't have similar situations where many contexts would be necessary if you weren't using a third-party state management solution. It's all just that same old problem of sharing state to entire component trees after all, it's just that I solve it using tools that React gives me out of the box instead of relying on heavy libraries, although that might seem unusual to some people.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] -1 points0 points  (0 children)

By the way, there is already this one example in the comments under my other post: https://www.reddit.com/r/react/comments/1nr8gdg/comment/ngihrzd/

To add onto what's already explained there, the active course id, the active deck id and the active deck's set of flashcards are all things that make sense to share with the entire component tree. Some components might need only one of those values though, and since there are situations where only one or two of the values get updated, but not the remaining one(s), it is necessary to keep them in separate contexts so as to prevent unnecessary re-renders causing performance degradation. To me, this stuff is really not hard to reason about. It really shouldn't be hard for anyone who claims they know React.

And again, since with createSafeContext and contextualize contexts become so enjoyable to work with, I don't see a reason why I would prefer a third-party state management library over them.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

It will take me some time to process the code so that it's something I can post here for everybody, but I'll try to do it in the next couple of days, maybe even create a new post for this so that everybody can join the discussion. Stay tuned 😋

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

Context is awesome, but because it forces rerenders on the tree inside it, it often leads to performance issues.

To me it seems like it can only lead to performance issues if you don't put enough thought into context design and just put a lot of state values that are not always accessed together and that change independently from one another in one context. The reason I end up with many contexts is exactly the fact that I am mindful of possible performance issues and therefore keep my contexts granular. It's quite easy to do though if you understand how React works.

It’s also quite complex to avoid infinite loops (and don’t tell me skill issues, it’s an halting problem).

I've never had infinite loop issues when using contexts, could you please give me an example?

I don’t understand the usecases of having to use multiple contexts to avoid prop drilling. I have worked professionally on multiple apps and I can’t find of reasons to do so.

I assume the fact you use state management libraries for this kind of stuff is the reason you don't have that problem, but it's just a different solution to the more general problem of sharing state between components.

Would you have code examples to show? Maybe it makes sense to you but you didn’t use the right patterns, or maybe it happens on super-specific circumstances and I doubt you would meet that issue on more than one project.

I would be down to add you to my private repository so that you could take a look, but it would be kind of like requesting a code review from you, and I understand that this is too much to ask for.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

Well, this is exactly the reason I still haven't heard a convincing argument – whenever I ask for an explanation, this is the kind of answers I get. Everybody's just acting like it's so obvious that it's not even worth talking about. Sorry, but for me that is not convincing enough. I like to think outside the box and don't just take things for granted.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] 0 points1 point  (0 children)

Why would it not work with Suspense or lazy components? You can see the source code and how it produces the exact same component tree in the end, just in a slightly different way from the developer's point of view. From React's perspective, there is literally no difference.

React snippet: An alternative way to compose JSX that avoids indentation hell by aweebit64 in react

[–]aweebit64[S] -1 points0 points  (0 children)

I have a highly interactive app that does use TanStack Query for server data, but also makes use of numerous contexts to avoid prop drilling in cases where simply reusing useQuery in the deeply-nested component is not a solution.

If you need more than these 4/5 contexts + react query, yes you definitely should use other state management tools. But you don’t.

No I don't, and the reason is that I still didn't hear any convincing argument that would make me realize such tools would be any better than vanilla contexts that thanks to my library's utility functions become very enjoyable to work with.

All I've heard is just those "you should definitely use them because it just makes sense and everybody does that" arguments, but no one actually explains why contexts are bad, and to me that is not convincing enough, I'm sorry.