A few days ago, I wrote a post about a workaround/hack that I've been using in React to pass around components' state variables and functions. I k...
For further actions, you may consider blocking this person and/or reporting abuse
Overmind.js - the redux that should have been written in the first place.
It's not forcing an overly complex "functional" style with all its unnecessary wrapping a function in a function. You don't need 5 libraries just to be able to do async events. It just works as you expect it.
A random thought: there is a lot of hatred for OOP in JS circles - rightly so, since the language was not built for that (and neither for FP). However the idea of having
private
vs.public
fields is excellent and it is connected to global states: if you put any minuscule implementation detail into it, people are going to depend on them making the application hard to change (but it would be still "theoretically correct").Interesting. I haven't been exposed to Overmind before, but I'll definitely check it out!
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) :)
Have you run into rerendering issues with passing functions that update state via Context? I think one solution is to memoize the functions, but I'm still experimenting with that.
I'm playing around with something today and your question came to mind. If you want the context to be properly updated on state changes, you must be referencing it from within the
render()
function. I'm gonna do a follow-up post on this soon that will illustrate this. This might be part of the issue you've experienced.Looking forward to it!
And... here it is:
dev.to/bytebodger/a-context-api-fr...
It's not radically different from what I already highlighted. But it emphasizes the need to put the context references in the
render()
function. It also provides a few stylistic improvements to the first post...No, I haven't run into any issues with that. I haven't used it enough yet to say that there are no issues with it. But my initial tests haven't revealed any problems... so far.
I agree. And that kinda brings us full circle back to the original point of this particular post. The typical flow seems to go like this:
But this is why I'm excited about the Context API. By using that, you can keep full control of that value in its original component. No need to route all changes through a series of actions or reducers. In other words, you can let React's native
setState()
handle all state updates - just as it was originally designed to do.It is true that, with the Context API, those value do have to be "exposed". But that should usually be quite painless. In the case of state variables, if you're passing all of
state
into the ContextProvider'svalue
, there's no need to worry about making futurestate
variables accessible. It's already done.I had developed a pertty complex app without any state management tool, with Context API. If there are any downfalls where components are getting rerendered, we can definitely optimize that. We can surely architecture state with context API like there is no need of external tool to do that
I Love this improvement of state sharing while I prefer to keep even react out of the way. I would always prefer modules for shared state as you can dynamic import them and this way share state on module level inside components.
i would love to hear your feedback about this dev.to/frankdspeed/the-html-compon...
Thanks for sharing but,WOW. That was a lot of text. Probably a lot of the words could be removed and the idea would still come across just as clear if not more.