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).
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).
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.
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.)
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 nologToConsole() 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.
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.
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.
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.
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) :)
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
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
I fully agree with the sentiment. This is a mistake the React team keeps repeating for some reason (most recently with Hooks).
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'spublic 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).
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.
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.)
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 aconsole.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 thetheContextApiIsCool
state variable should possibly "live" in the<TopTier>
component. But one of the (lesser) points of my post is that, IMHO, if we havestateVariableX
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 callsetState()
on that value should also reside in the same component.I agree with where state should live. I was saying a function is not state (unless it is dynamically changing). Again, purely semantic point.
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.
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.
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 therender()
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...
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) :)