Ryan Florence - Compound Components

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Am I wrong to assume compound components means readjusting the child component's props through cloning rather than adjusting the child component itself? The latter seems more react-like and compounding components seem a bit more complex than using context despite hints of context's deprecation.

Compounding's intent seems to be allowing smaller components to be untouched therefore avoid bloating it with unnecessary amount of conditionals and leave modifications to the parent. This would mean the smaller component, <Tab />, in this case to be more reusable without reimplementing it specifically for <TabList /> though it seems less likely <Tab /> itself would be used outside of <TabList /> which gave me the impression it doesn't improve <Tab />'s reuse.

If I am on the right track would anyone mind giving an example where compound components would excel rather than use of HOC/context?

👍︎︎ 2 👤︎︎ u/renshenhe 📅︎︎ Jul 27 2017 🗫︎ replies

This goes against the declarative composition that you should follow in react.

Don't clone the elements, create another component that is going to take care of rendering the right sub components.

I'm not surprised that Ryan is presenting this technique. I've seen many things in React Router that did not really followed the react way of things.

👍︎︎ 3 👤︎︎ u/franz899 📅︎︎ Jul 25 2017 🗫︎ replies
Captions
anyway we've got a little tab app here let's look at the code for this alright so color scheme decent out there for you okay let me get rid of some stuff down here No so here's our app it's a tool using create class it's my old friend I just can't seem to put it down here's some tab data just an array got a label and a description burritos coconut core Emma and here we've got our tabs let's go look at what tabs does we can see here that we give it an array of data that has a label and appendage description let's see what the code is doing it's got some state it's got an active index so which one of these is the active one we've got this thing to select the tag tab at an index we can render some render all the tabs are into the panel and here's our main render method rendered the tabs render the panel looks good so I ship this app and then my coworker Jen comes over to my desk was like hey Ryan I just got the designs for the new page from the designers and they did the same tabs that you did and I was thinking that maybe you had some code that I could just use so yeah there's a tabs component just feed it some data and away you go and so Jen imports tabs into our app and renders it and everything is great and then the next week Jen comes up to my desk and says hey Ryan I got some more designs from the designers and of course the change that they want the tabs on the bottom like okay she says his can yer can the tabs do that like I know but maybe we could do like well let's do sorry about the flashes maybe we could do something like tabs on bottom see what that would look like so we'll come up to our cabs this is their inter method of tabs so I've got the tabs here and I want them on the bottom not on the top but I want to be able to like pass it true for tabs on bottom or faults and have it switch so what's really cool about react is these divs here this JSX it's really just a call to react create element you know I'm going to switch the color scheme this one that I think looks a little bit better in here it's really just call to react create element div and this thing has a return value which actually is just this object thing that has like some props and some stuff but it's really like you can you can you can json stringify this thing if you wanted to and see that it's just like this this object and maybe we'll look at that in a second but the point is this create element just returns just this object it's not it's not in the Dom it's not a Dom element it's just this object that describes some UI but it's not UI yet it's just a description so these these divs get transpiled to react create element and they have return value so I mean we can see it here this div is being returned that's that's our return value of this method so I can grab these tabs and move them up into their own variable and down here I can render them it's just data our UI is just data just an object here's my panel down here I can render the panel save that will see a refresher in the background and it all still works at least I hope it does ok gettin done some react check this out let's look at the panel it's just this object yada-yada-yada it's got some props on it I'm doing something kind of silly up here that I need to stop doing to get rid of that warning let's say some stringify and see what that looks like if you're ever in a JavaScript interview and they ask you silly questions like what's the circuit argument to do some string if I tell them the answer is null oh it's a circular structure it's not always circular let's do a div here am i circular are they all circular now did something change on me bummer well can't do that trick anymore the point is it's just an object it's an object that react takes and figures out how to make real elements out of it but it's just the description and so when we have these descriptions we can move them up into variables and now I can do something like this I could say if this dot props dot tabs on bottom yes no well if I want them on the bottom now I can return an array outer I guess it's a return out of this expression put the tabs on the bottom then I'll do the panel on top tabs on bottom and vice versa check it out tabs on the bottom now I can come back down here and switch this two tabs on defaults and they'll be on the top ship it so we had this little bit of code we made our render method a little bit strange looking it's like super sad because of these this ternary look it's like sad on the Left sat on the right but I'm very happy about it someone asked me earlier what what things people struggle with the most in our workshops and I just said JavaScript I'm going to I'm going to add to that and say JavaScript expressions because once you're in JSX these are all just function calls and as soon as you put that curly in there really all you're saying is here's an argument to the div function or to the create element function and so when you're passing an argument in we can't have statements and blocks everything's got to be an expression so you'll end up like finally understanding why list people love Lisp so much as you get crazy with your expressions in your JSX but you just get you get wilder and wilder with that the longer use you use react okay so that's fine I added a new prop did I bring my water up here I didn't someone want to grab me one those waters or maybe find mine that was sitting around yeah that's it it's not mine it's as good as any other so a couple days go by and Jen comes up to you and she says I just realized that in our UI some of these tabs get disabled so how can I set some of these tabs to be disabled is there an option or something and I'm like no there's not but we can make one goes to a jQuery UI does we're here who's use jQuery UI a ride still one of the best UI libraries out there this is battle-hardened let's look at their API check out these options active classes collapsible disabled okay so you select an element you turn them into tabs and you pass in an array of indexes or indices depending on which side of science you fall on so I pass what these indexes and then it will decide okay this one's disabled this one's not seems good enough for me actually do that when I'm like building a new component in a new library I always go to jQuery UI and look at their options and then I know like what I'm going to run into and what kinds of use cases people are going to have so we'll do disabled let's disable the burritos because let's be honest burritos are just big tacos so we passing disabled up here where I'm rendering my tabs this is rendering all the tabs we're mapping over the data we get a tab and an index and we know if it's active by checking our State against its index return this thing ok ok ok here we go is disabled equals this dot props dot disabled dot index of index is not equal negative 1 ok which me like I'm in one ternary already is disabled styles dot disabled tab otherwise is active okay looks good maybe we could put some things on some new lines maybe and that'll help yeah it didn't really help but I'm really happy that these line up though okay got the style working but it still changes when I click on it on click if we're disabled don't put anything in there otherwise let's use this function I'm clicking if you can't tell that's a nice click sound yeah no oh that was genuine those organic how many click event handlers are there in a react app one yeah we could have a thousand elements that we put an on click that we assign it on click to in react it's almost free the only expense as far as I understand it is adding a property to one of these element objects that's it that function just kind of sits there and then when you click react has an event that's watch it has a click event that's just watching the whole document and it sees the source element and it's the one react is one who created the real Dom element I can't see you that feels better good to see you and react created that element and so I can say ok this element got clicked I made it what's the virtual element on the virtual Dom that I use for it and it finds it this is OK does this have an on click on it oh it does and so it creates a synthetic event and sends it if it doesn't find it there then it goes up in bubbles and bubbles until it finds one or it never finds one and because you don't have any click handlers on it so really smart we used to manually do that the event delegation in mootools and jQuery and just the Dom now we get this beautiful API of just saying on click call this function and react does the the perform it for us so if I'm rendering over and over and over at these tabs and I'm changing it to being disabled and then enabled and then disabled and then enabled there's not really a big expense here like there would be if I was adding an event and removing an event and adding an event and removing an event that'd be that'd be hard on the browser but with react it just handles that all for you so it's kind of cool hmm I don't know this tabs components getting a little bit weird seems like every time we need to use it quite a bit of code has to change in it if you want to find the end of the road let's go look at one of the best I think probably the most used JavaScript component ever the date picker from jQuery UI I'm friends with the developer who made this mark and he he made it before jQuery and then turned it into a jQuery thing then turned it into a jQuery UI thing so this thing has been around for like ten years long time maybe maybe even longer than that so it's been through it all check out the options so this is not a criticism of the date picker this is a criticism of how we used to compose our UI with these imperative libraries like jQuery and some server rendered HTML I'm going to call out some of these options and I want you to think about what they might have in common alt field alt format append text button image button text close text current text date format day names day names man be named short hide if no pretext alabone month names month names short next text brief text stylistic okay there's another word that you're looking for but that's it that's on the right track a lot of them end in text right so stylistic like what we're rendering so all of these all these options are saying what do you want me to render for the names of the month because if you localize your components you're at like you've got to translate that to different languages you can't just call you know dot date picker on some jQuery element and be done with it that thing is going to have some month names in there you need them to not be in English you give them to be in a different language this takes over all of rendering it owns all of rendering and so in order for you to get in there and do some translations do some localization or do anything custom with the styles or rendering at all they have to open up all of these options it's ok what do you want the day names to be what do you want the clothes text to say and so what happens to these components that own rendering is they end up with a list of options like this who here in the room is sitting here thinking about a component in your react app that just keeps getting new options anybody and so yeah getting getting a few hands out there it always happens and then you just add another option add another option and every time another team uses this component they need two or three more options on it this is a lesson that we learn the hard way with react water you gave us this route config and some components and then we did all of the rendering of your whole app essentially but you need to pop in there sometimes and say well I've got like a user ID that I need to send as a prop to this component but I can't because I don't get to render it you do and so we made options we opened up little holes for you to be able to render things there's a different way to do this a way to give rendering back to the person using the abstraction because these abstractions they're there to share some logic but not necessarily some presentation so maybe we can have these things just do the logic part and the app can still handle the presentation of it so I'm just going to trash all this it's time for a rewrite I hate rewrites except when you want to do something that's fundamentally different and in this case we do hang on in just a minute we'll get there yeah keep going okay I'm going to uncomment this all out and what if we had an API that looked more like this I say I've got some tabs and here's my tab list here's one tab here's another tab if it's disabled I can just say it's disabled right there I don't need to pass in this weird array of indices I can just say this one's disabled then I'll have my panels here all right this is kind of what it would look like if if maybe the Dom shipped with a native tab component we've got something a little bit similar to it select then inside with you options it's even option group stank right the Dom has this idea already in it in some of those built-in elements where we've got this parent component that wraps some other ones and then there's some stuff that happens inside of there right these options have click handlers on them right the browser knows oh if I click on the select and then I'm scrolling down I click one of these I'm going to change the value of the select so there's some interaction with the data flow in this little system of components I call them compound components is components that compound together into just kind of one idea so the Select drop-down has these three components select option and option group that compound into one component idea one component system and there's state that lives inside of that system as well it's not application state this isn't stuff that we want to put over in Redux or in some global store somewhere it's not component state because my component has its own state here this is like says its own little it's a little system this little world of components that have some state that we need to shuffle around so let's see how we can do that I've got all these these components kind of sketched out up here they don't do anything yet so we've got the tab list it just drops some Styles on there and then renders the children we've got the tab style rendered some children tab panel some so I'll render some children yeah they don't they don't really do anything here's our state this is that this is a little compound a component state that we were talking about so this is the thing that abs really deals with what's the active index and then it deals with which panel to show based on that active index and then it adds the click handlers to the tabs so that's everything that I don't want to do in my app I don't want to have to add click handlers to these on click do something I don't want to have to decide which one of these panels to render I just want to tell tabs here my tabs here my panels you handle the rest so I'm going to save this and we'll have a mess over here we'll have nothing even rendering and no errors means I'm probably not rendering there we go or I'm using promises just kidding I'm not kidding but I didn't mean to make you sad all right there we go so now we're rendering all the children we don't know what's active none of these click handlers work so we got to put all this stuff back reconstruct this thing let's start with picking the right panel so right here in cab panels I don't want to render all of these children I want like an active index let's say it's 1 2 and then I only want to render that one remember this is just data this thing's an array it's sitting there it's got an active index okay I didn't get that from the right place but I got the rendering to work so that's what I'm after I want to figure out what is the active index I could get it from crops so I guess I come down here to the app and I could say dead panels active index is let's do zero so tacos should be alright so talk was is active back there we already talked about though that this state is not anything that the app should care about the app doesn't care what the active index is this is the point of tabs is to know what that index is and to render the right thing so props aren't going to work right there there's a different way to pass a prop to tab panels we can do it secretly covertly without it even knowing so tabs owns that state I'm going to a split pane here so we can see our usage below so these children four tabs is tab lists and tab panels you underneath the hood right the the app can't even see this happening so if I save this we should still have tacos being active let's switch our active index to wine just to prove that we've done this there we go and now we're on burrito kind of cool so clone element is a way for us to take an element this one this this tab panels it didn't have an active index drop yet but we're able to give it that property I always want to say opaquely or transparently right now I don't know which one to say we pass it that prop opaquely because you can't see that it happens but if it's transparent then you can't see the thing like I can't see the prop it's like it's trans inadvertently I meant to do it I guess so I don't know which word to use there so we passed the prop under the hood Co covertly yeah advertently covertly let's get back to my childhood okay that's cool let's get it to the tab list so that we can we'll always forget that that's a thing now so that we can see which one of these is active so the same thing I just want to get it from my props so let's come up to tab list here's tab list and mmm this is kind of interesting I know I need to send that prop down to cap panel so let's let's just clone it here so we'll say else if child type is tab list let's yeah I'll do that you can fight with me in code review but I'm going to copy paste so now up here in tab list I've got the active index from my props if this just freaked you out do a google 4d structuring in JavaScript and then you'll still be scratching your head for like three days and then one day it'll click you know aha that is cool all right so now I know the active index because it got passed covertly and now I need to clone because I really need to get that down to here so the tab is saying hey I need to know if I'm active or not so I need it from this dot props that is active down into tab as well so calves the parent let's come down here again and see how we render all this stuff so tabs is the parent it owns the state it clones the tab list passes the active index and then we need to clone these and tell them whether or not they're active so this is tab list it's going to clone its tabs we'll talk about why react out children about map instead of just map maybe later but maybe not here in the workshop tomorrow absolutely we will okay 800 by 600 so far we're doing we're doing all right so I'm going to return clone element and I want to tell it that's what I was doing wrong I mean that's going to get me okay I'm going to return a cloned element here and I'm going to tell it whether or not its active so I can't just pass it active index because then when I come down here into tab my index has no idea what its index is in this list it's just an item in the list so we don't want to give it the active index we need to just tell it whether or not it's active and we know that one level above we got the active index from our parent we know the index of the children because it's our data and map tells us that index so now I can tell you or I can tell it it's active if the index in this apparently make sure that we can all see every won't be able just can't get there with 800 by 600 there it is okay so we're getting this index passed into here so font-size still big enough okay so now we know by checking is index equal to active index and we're reading right there so this should be good cool shrink that up so we can see that again is disabled it's just coming from our props so right there I guess we called it is disabled that's cool before we had to pass in this weird array and introduce a little bit of indirection right we're going to say okay at the position 1 is something going on like there's this indirect relationship between where we're defining something is disabled and then where we actually deal with it here we get to define it exactly where we're rendering it it's pretty cool so I should see this yep that one goes to being disabled yeah so the last bit here is to get those click handlers back so I need to go to my tab so here's my tab and I'm going to say and this is not accessible so if you are into accessibility like I am that's a different talk but right here on click we want to deal with this so what do we do when we click we need to tell somebody to do something right we don't know we don't know how to change the state of tabs but that's what we want to ultimately do we're tab we were entered by tab list and we're in that what surrounded by tab so we need to get up here and change the state up there how do we typically have a component when something happens inside of it tell the outside world yeah call back a prop call back we give it some sort of on something so I'm going to say this stop props dot on activate it's a tab it's being activated might happen from things other than a click so I'm not going to call it on click this is a different different event so when this div gets clicked we're going to call this type process dot on activate and we get that from our parent tab list so we pass that prop in we know the index here we don't own the state either so we got to do something with that same thing to stop drop so I'm going to call it on activate cab to disambiguate it from this stuff drops out on activate now we're calling up and saying hey this tab got activated and here's the index we know the index because it's right here in our map this is so cool because for so long we used to like put data attributes on our elements like data ID and stuff like that right and then want to get to clicked we check event target get attribute data ID and now we knew what data to work with and what request to make or what whatever we needed to do with it here we own rendering these things are just plain old objects before they go to the DOM and so as we create them we can wrap up their data like their index right here we don't need to rely on indirection to get that we don't have to indirectly refer to their index by putting it somewhere else first and then retrieving it off later we can directly use the index right here where we create the UI in the first place now that's one of the things that I love the most about react is that there's no indirection everything you have a direct reference to things a direct reference to a function a direct reference to a react element there's not a template with a bunch of indirection in it okay so on activate tab that needs to come from tabs because tabs rendered the tab list so if we're in a tab list we're not going to send just the active index you see had I gone and made this dry in the first place I would have had to go and undo that dryness later we call that brittle code so dry it's brittle anyway or they pass into this I got my I got my soap box on activate tab I'm going to get an index that comes in here I own the state I'm getting an index so let's set it active index is index I'm going to do one more fun trick and es2015 back of index active index both of these are the same thing so I can just shorten it okay so what's happening here it worked even though there was a bug maybe maybe not Oh burritos is still burritos can still get clicked we missed something in tab if we're disabled do nothing okay you know what the people on the video didn't get to hear the click there you go video Watchers that felt really intimate I was like down there on my face I'm sequence will be weird when I watch this myself later anyway we got the click handling happening now so what's going on here let's review I click a tab tab arranges a div and tab puts the click handler on it it got a process on activate past covertly to it by tab list tab list new the index of the tab and then it called props on activate tab covertly that was passed to it by tabs tabs gets that index all the way up and then set state so we built now this little system of components these compound components that are sharing state down this tree of components and sharing state back up this tree of components and the app doesn't have to deal with any of it it's also not shared State right there's there's a lot of different kinds of state in an app we got the Redux stuff shared sit that we put in a global store we've got component state that's like hey my sidebar is open or it's closed and then if we want to make really really reusable components there's this third state that lives in the middle or it's like this is shared between a group of components but not necessarily the whole app especially because well check this out I heard you wanted some tabs and your tabs so it's not appstate it's not necessarily app component state it's a compound component state and we can get that through this little system with a react clone element and pass it those extra props now check this out Jen wouldn't have even come to my desk when she needed the tabs on the bottom should have gone like this Tad's on bottom thrown h1 in there would imagine the jQuery UI option for that interrupting text or what would we call that option I need just some rent arbitrary divide node Y okay this is not the angular meetup I don't know what you just said pretty sure the syntax that would be like course multi-select transclusion is that what you said okay oh good angular 2 has angular 1 jokes now welcome to the club Oh remember what I said I don't like them when they recorded now like someone's going to post this and be like look at Ryan he's a jerk boycott his workshops any others a cool cool framework I interviewed misko about a and asked question why did you make it I gave a talk at Jazz comp in 2014 about it was called M Bueller after myrrh bone ember angular backbone polymer and react they gave me like 25 minutes or 30 minutes to talk about all five of those it was not a very good talk people told me afterwards it was good it wasn't not enough time well the scope was just too big and anyway he said the reason he built it was he wanted designers to be able to create interactive web apps and nailed it like absolutely nailed it so many people got enabled by by angular and started building really great apps so I really don't mean that it's like a slight sometimes people think that's a slight it's not great framework yeah that's compound components anyone sitting out there thinking about like one of your giant components in your app that you've just not been able to quite figure out how to make this thing not so big and weird this might be it this might be a pattern that you can employ there the whole point of this is to give rendering back to the app we don't want to own how all these tabs get rendered what order they're in if they're on the bottom or the top we just want to funnel that state around and add those click handlers to get that state up and down and show the right stuff but we don't want to own all of the rendering we want to just hand that back to the to the app some of you however looking at this thinking you know I really liked this one there's a whole lot easier I'll check it out let's call it data tags let's get our tab data in here you have to recreate it all now what we call a label with like tacos and then a description had a P tacos are good burritos - I'm glad that Spanish isn't like English we're fine with just the two so check this out let's make a data tabs and we get our data off of our props return some tabs I almost got it and then we have our tab list inside of our tab list we map over the data make a tab tab dot label I think is what we called it down there let me do the same thing for our tab panels and then we do a tab panel in here instead and we use tab description and voila we have some upset syntax I know that I found a good abstraction when I can do the old pattern with it right I've got this code that's here a refactor to something that's more powerful all these other components but I don't need to go change the whole app anywhere that we had tabs I'd have to import data tabs now on all those files and now a data tabs I can just dig that thing I used to do but there's a whole different implementation underneath that's a lot more flexible and weak we could do our crazy tabs on bottom stuff in here if we wanted to and completely re-implement that old API yeah that's it that's compound components I'm going to end it there and we can do questions afterward but don't want to record the questions on there yeah thank you I appreciate it [Applause]
Info
Channel: Phoenix ReactJS
Views: 35,392
Rating: undefined out of 5
Keywords:
Id: hEGg-3pIHlE
Channel Id: undefined
Length: 43min 19sec (2599 seconds)
Published: Sat Apr 01 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.