DEV Community

loading...

Discussion on: Throw Out Your React State-Management Tools

Collapse
isaachagoel profile image
Isaac Hagoel

Thanks for another interesting post. I wonder, does 'logToConsole' and the other 'methods' belong in the App state?
I have never used the context API but it looks like you could have stored them in the context outside of the whole 'state management' system, right?

Regarding

the "problem" is that the declarative syntax is usually implicitly tied to the render() process. But there are times when you want to leverage such functionality without depending upon the rendering cycle.

I fully agree with the sentiment. This is a mistake the React team keeps repeating for some reason (most recently with Hooks).

Collapse
bytebodger profile image
Adam Nathaniel Davis Author • Edited

Thanks for the feedback! As for the location of the methods in <App>... yeah, I get what you're saying. Whenever I'm writing a "real" app that doesn't exist purely for demo reasons, I never put any real logic (or methods, or state) in <App>. To me, it's a kinda "best practice" to treat that initial "springboard" component as a completely blank container whose sole purpose is just to launch the rest of the app. No one's ever explicitly told me that this should be done as a "rule". It's just something that I feel has kinda become understood outta (too many) years of experience.

For me, a salient analogy would be Java's public static void main() method. Every Java app must have it. And yet, it's kinda an amateur practice to shove a buncha logic into it. (In fact, I think it's kinda amateurish to put any real logic into it.) It's only "real" function, is just to launch the rest of the app. When I create React apps (for me, or for employers) that I expect to actually be deployed into a proper production setting, I always treat <App> like Java's public static void main().

The only time I break this "rule" is when I'm throwing something up purely for demo purposes (e.g., like on StackBlitz).

Collapse
isaachagoel profile image
Isaac Hagoel

I get you point. What I meant is that usually methods that can't change don't belong in 'state'. They are not part of anyone's state :)
They can still be part of the context though (I guess).
It is a minor point and not directly related to what you were trying to illustrate. It just popped to me as I was scanning through the code.

Thread Thread
bytebodger profile image
Adam Nathaniel Davis Author

OK, I think I see what you're getting at. I will freely admit that I'm still figuring out all the ins-and-outs of the Context API. But I found, in my initial setup, that I had to put references to those functions into the associated component's state for the whole context-reference thing to work properly.

Now... am I stating that as an "absolute truth" that must be done to make the Context API work?? Definitely not. I've been doing dev for 20+ years. But I literally started experimenting with the Context API yesterday. So it's perfectly possible that I didn't actually need to stuff those function references into state to make the whole thing work. That's just the way that my solution "ended up" when I finally got it all working.

(Side note: I will freely admit that, in some respects, the Context API was a little bit challenging for me to "grok". I actually played around - with a dozen-or-more approaches - before settling on the example that I put in the demo.)

Collapse
bytebodger profile image
Adam Nathaniel Davis Author • Edited

Continuing that thought, if this were a "real" app, most/all of that state/functionality would be shunted down into <TopTier>. That probably woulda been more effective for the demo, cuz the whole point was just to show, "Hey... See? We're skipping the <MiddleTier> component and porting values/functions directly into <BottomTier>."

Of course, if this were a "real" app, there would be no logToConsole() function. No self-respecting dev would create a wrapper function whose sole purpose is to perform a console.log(). My only purpose in including that (silly) little function was to demonstrate that, "See! We're calling an <App> function directly from <BottomTier> - and we're doing it solely through a Context API reference."

Now, as for the toggleTheContextApiIsCool() function, there is a specific reason why it was placed in the <App> component. I can easily accept the idea that the theContextApiIsCool state variable should possibly "live" in the <TopTier> component. But one of the (lesser) points of my post is that, IMHO, if we have stateVariableX that "lives" in <ComponentX>, then, whenever possible, the functions that update that state variable should also "live" in <ComponentX>.

So to put this another way, in this example, I don't particularly care where the state variable of theContextApiIsCool resides. But wherever you choose to place it, the subsequent function(s) that call setState() on that value should also reside in the same component.

Collapse
isaachagoel profile image
Isaac Hagoel

I agree with where state should live. I was saying a function is not state (unless it is dynamically changing). Again, purely semantic point.

Thread Thread
bytebodger profile image
Adam Nathaniel Davis Author

Yeah. I get that. And I think I answered that in my other answer to your comment. To be frank, the only part of my proposed Context API solution that feels a little "weird" or... "off" is the stuffing of those function references into state. I'm not sure if I had to do it that way. But that seemed to be the way that I "had to" do it to get my demo to compile/work. When I tried it without the state-function references, it broke when I tried to invoke a context-based function that resolved to a function that invoked setState().

(Does that make sense?? It's fairly clear in my mind at the moment - but it's one of those concepts that's kinda confusing to try to spell out in text.)

That being said, once I got it working, I didn't necessarily dislike it. On some level, it's almost kinda cool. Cuz, aside from trying to find a better approach to (what is, IMHO) the bloated mess of Redux, I kinda liked the idea that we can (or... have to) explicitly define the functions that are available via the Context API.

I dunno... I could probably be convinced that it's somehow "sub-optimal". Or that it's somehow an "anti-pattern". But, for the time being, it kinda seems like a good thing.

Thread Thread
isaachagoel profile image
Isaac Hagoel

Thanks for all of the replies. We were just replying to one another too quickly so I missed your previous one :)
Maybe I will play with it a bit too (will try to make time). As I probably expressed before, one of my main issues with React (some other frameworks have that too) is its insistence that everything is 'ui related state' and rendering logic. I find this to be thoroughly lacking.

Collapse
bytebodger profile image
Adam Nathaniel Davis Author • Edited

As for the whole "rendering cycle problem", I'm glad that you understand my point. Lemme give you a more tactical "real world" example that I ran into recently.

I had to build a React app with backend/API connections. And I was specifically requested to craft those API connections via GraphQL. To be frank, I'd read about GraphQL before, but I hadn't actually implemented GraphQL endpoints before. (Obviously, I'm all-too-familiar with REST.)

So, after doing some basic get-up-to-speed reading, I did what a lotta React devs do - I started playing with some of the major React/GraphQL libraries that I could leverage on NPM. I wasn't using some esoteric packages that have minuscule downloads. I was trying to use the "official", "approved" React/GraphQL libraries. The ones that are either written, or explicitly endorsed, by the core GraphQL team.

But I kept running into one core problem that bugged the hell outta me. All of the "official" libraries wanted me to embed/invoke the GraphQL components directly in the render() function.

Once I got everything properly loaded up and connected, I noticed that the damn components were constantly calling my backend API - sometimes, two or three times - for a single load of the application.

To be honest, I don't think the client woulda really given a shit. But it bugged me greatly. I HATE invoking unnecessary API calls. And when I'm doing an SPA, I take great pride in trying to minimize any unneeded renders and/or API calls.

Of course, one of the "hallmarks" of React development is the (near constant) struggle to halt unnecessary renders. So, at first, I attacked it as a "stop the unnecessary renders" problem. But no matter what I was doing, I just couldn't get it to where it would only call the backend API once. It always called the API at least twice.

Finally... I told myself that this was just stupid/wasted effort. I mean, in the end, a GraphQL query (or a REST query, or a SOAP query, or any kinda query) is just text that's gotta be formatted in a very-specific way. So I ended up just ripping out the React/GraphQL libraries and writing the queries manually - in a fetch() - that didn't "live" in the render() function.

Once I did it the "manual" way, it worked beautifully. It called my backend API no more than the exact number of times that were necessary to provide the needed functionality.

And yet... soooo many React libraries just assume that you're gonna invoke all their custom components in the render() function.

Sigh...

Collapse
isaachagoel profile image
Isaac Hagoel

Thanks for sharing. I didn't use graphQL yet (beyond playing around in a sandbox) and didn't know the problem extends to it. I love the fact you didn't just accept it and went back to basics:)
I think one major thing a lot of framework users don't understand is that the people who make these frameworks are just normal developers that don't like to other people's frameworks (so they make their own) :)