Placeholder Image

字幕列表 影片播放

  • So, uh, like I said, my name's Tanner Lindsley from Utah.

  • And I love Java script so much.

  • Uh, I also love react even more.

  • I'm happy Thio be able to talk about react at a Java script conference.

  • I'm on get hub.

  • Probably way too much riding way too many open source libraries.

  • I'm also on Twitter and YouTube.

  • You come say hi to me there if you want.

  • Um, I know that you're all probably feeling like you can't see my screen.

  • Yeah, you're probably feeling a little bit like my son here, you know, just really tired and wanting to consume that last little bit of javascript on.

  • Just do your best.

  • You know that?

  • It's ah, I know it's hard, but we're gonna make it through it together.

  • Um, because today I'm talking about react, tooks and more specifically, custom react hooks.

  • And I'm so excited about this.

  • I hope you are too.

  • I know it's been a noisy year.

  • Um, you guys probably feel like, uh whether you're whether you love react or not, you probably feel this way.

  • So I'm gonna do my best today to make sure that this is not just another React Hopes tutorial.

  • It's going to be fast because I have a lot of content to go over and there's gonna be a lot of code.

  • Um, hopefully, it's a lot of fun.

  • I'm gonna be saying, used a lot, So it's hard.

  • It's a tongue twister, you'll see.

  • So before we get into, uh, my the bulk of my talk, let's go through react hooks basics really quickly.

  • Uh, classes are out.

  • We no longer write with classes.

  • Everything is just a function now.

  • And that is so nice.

  • It makes us so that components that used to look like this to use state don't have to look like that anymore.

  • We could just use you ST so much better, in my opinion, where we can use use reducer class life cycles are no longer a thing.

  • Oh, I'm so glad to be done with these things.

  • Um, also, we don't have to dio manual change detection anymore.

  • With the old class life cycles, we can just use the use of fact function from react, passes some dependencies and everything will just synchronize for us.

  • It also comes built in with memorization and computer state things around memorizing values and functions Where before what?

  • We could do this with react before, but now it's built in so we can make sure that we're only computing or doing work when we need to be doing it.

  • Uh and last.

  • The most important thing that I'd like to talk about today is custom hooks, which custom homes, really just a function that runs inside of a component that can run other hooks or other functions like you state used, reduce or whatever.

  • And they could be recursive.

  • They can go forever.

  • You can have, ah, custom put, calling other custom hood calling the custom hook.

  • Call another customer and everything will just come back in and it just works.

  • It's amazing.

  • It makes us so patterns like this that before we had to nest all these callbacks and create hired or components and render props and, you know, functions as Children.

  • All that gets to go away, and we're simply left with a couple of function calls that we can make inside of our inside of our components.

  • Custom hooks are so powerful, I believe that they give us a couple more afford Ince's that we didn't have when we were using class components.

  • Um, I think that they encourage us to build our own solutions because it makes building our own solutions easier.

  • The AP I is more expressive.

  • I I also believe that building hooks custom hooks way more portable and share a bowl than building custom components and trying to move logic around with components.

  • Another great thing is that custom hooks can be component, aware and abstract, essentially anything that we want them to just makes him very powerful for integrating with essentially anything we want to integrate with the outside in the outside world, away from react.

  • And last but not least, I think they highly encourage rapid adoration for some of the reasons that I'm gonna talk about today.

  • So the biggest reason that I believe that all those things were true is beat that hooks, Uh, with the introduction of custom hooks, I think that we're going to see or have already seen a return to components handling, user interface and hooks handling business logic.

  • And if this seems like a no brainer, you're ahead of the curve.

  • And if it doesn't, um, that's what I'd like to talk about today through so many migrations of old projects, two hooks going 100% hooks and new Greenfield projects where was able to experiment with new patterns around custom hooks and see, you know, how far can we push the limits of this new A.

  • P I I've discovered a couple of use cases that have really sparked new interests for me.

  • With this hooks A p I, um most of them around building portable you, I utilities.

  • Ah, few of them.

  • Well, actually a lot of them around Managing Global State and Server ST and also encapsulating business logic for our applications inside of these custom hooks.

  • And I want to share with you a couple of these use cases today to show you how custom hooks can really change the way that you think about writing your applications and to be more productive.

  • Why not start off with something that is so hot right now?

  • You can't ship a nap without it, and it's sometimes it's the first thing that we all think about.

  • Probably the most important thing we all think about as dark mode.

  • You can't ship without it, right?

  • This is so hard right now.

  • Everybody wants a dark mode.

  • You know, everybody's got that toggle at the top of their sight.

  • Go between light and dark mode.

  • Um, it's pretty easy to do.

  • You can just set up some state and toggle between with a function of set is dark to true or false.

  • It's really that easy.

  • I wanted something a little more sophisticated for my dark mouth.

  • I don't know if you guys have ever seen match media, but it's an A p I that you can use to match CSS media queries in JavaScript and detect certain things about the user's environment.

  • Right here we have a match me that's detecting whether the user prefers a light or a dark theme, and we can actually set up our state to start with.

  • That s so that if the user has a darker lightning preference on their device, are state now starts with that preference and they can toggle between it.

  • But who has this enabled on their computer?

  • It's auto mode ideo and I love it.

  • And I thought wouldn't it be amazing if we could make it so our light and dark themes would actually react to that automatic mode on our devices, turns out.

  • It's not too hard.

  • Weaken taken effect and put our match media inside it and listen to changes to that match media and update our dark variable whenever that changes.

  • And if we put all that together inside of our app right here, it's all just handled for us.

  • Now the sun sets, we go into dark mode.

  • Sunrises, we go into light mode.

  • It's actually pretty fun to play with, but the problem I see here is that a lot of this logic is now taking up space in my app component.

  • And I don't really like that talking about clean code opinions here.

  • But this, I see, is a great example of where we can make our first custom hook.

  • We can actually just take all of that logic and rip it out into its own file.

  • We'll call it used dark mode, and everything in here is exactly the same.

  • Except for we're returning is dark at the very end, and that makes this use dark mode hook extremely versatile in our app.

  • Now we can import that file call, use dark mode, and it's gonna give us true or false.

  • But it's not just true or false.

  • It's true or false that changes over time and all of the life cycle in updating and all of the intelligence of that dark mode is built into that hook, and all we have to do is call it.

  • And that is our first customer for today.

  • That's what everybody clubs.

  • The next thing that I thought every application needs.

  • Everybody has written this 1000 times clicking outside of an element.

  • And I don't know why the browsers don't make it easier to do this, Nate natively.

  • But, uh, you know, I feel like I'm implementing this everyday clicking outside of a menu or ah, motile or whatever, right?

  • And then you want to do something to it.

  • I thought, What about?

  • You know, there's not a better excuse to write a custom component for this, and I thought we might as well just made the assumption that this is going to be a custom hook right out of the gate.

  • So I assume that I could take a ref created by user ref, put it onto whatever element I want to keep track of and then have a handler something like, you know, Consul, logging.

  • Hey, you clicked outside of this thing and I could pass those two things to this use.

  • Click outside hook and it would just work.

  • And I wrote this actually, before I wrote the use quick outside hook to see how reliable it was to design a P eyes around hooks before even writing him.

  • It turns out it wasn't too difficult.

  • We start with the function signature we throw in an effect that's going to set up our event.

  • Listeners on the document for women clicked on the document.

  • Those call our internal listener here that checks to see if the target we clicked is inside of the element that were tracking.

  • And if it isn't it calls or call back.

  • Turns out it just works.

  • Um, and we would probably use this for a little bit and think, Wow, this is great.

  • I'm a genius, but eventually you might notice something very strange.

  • There's something fishy going on here, and it has to do with Theo used effects dependencies.

  • If you're using.

  • Yes, Elin, plug and react hooks, which you should be 99% of the time.

  • It does exactly what you wanted to it.

  • Auto fills effective dependencies for your hooks, so helpful.

  • I love it.

  • You can just hit save.

  • And if you have excellent set up, it will just do the changes for you.

  • And that's what I did here when we built our hook here on the right.

  • You can see it automatically filled in the call back in the l ref down there in our dependencies.

  • But the problem is that the callback is changing every single render from where we're using it on the left side.

  • Here on click outside, we're creating every single time, which means that those event listeners are firing off every single time that we render we're adding and removing those event listeners.

  • That is not cool.

  • Well, it turns out it's pretty easy to just wrap that and use call back from react, and it's going to make it so that that callback function doesn't change unless the dependencies change.

  • There are no dependencies yet, so nothing's going to change.

  • And it will stop, you know, creating those event listeners over and over and over.

  • But this begs the question for me.

  • Why?

  • Why do I have to have that use call back around that function?

  • Um this probably in advance edge case, but I feel like there's nothing inside of my use click outside hook that cares about this callback changing.

  • Um, and I decided that that's part of the A p I.

  • So I wanted to get rid of it.

  • And one of the easiest ways to do that is to Ah, instead of referencing the callback directly, we can use this pattern of tracking the latest version of that call back in a ref itself.

  • And when we do that, the callback ref is now added to the dependency array, which will never change but the value that we hold inside at will.

  • And that way we're only setting up and tearing down that event listener when we actually need to, when the element that we're tracking actually changes.

  • So now there's no call back, we can pass whatever function we want into that use, click outside hook, and that is our second custom hook for today.

  • What's fantastic about this is you can move this around your app.

  • You can import it to as many components as you want, or you can just copy and paste this into another app.

  • There's no external dependencies other than react.

  • I I do this all the time.

  • Greenfield project.

  • I'm like, I really love that hook.

  • I go back to the last project that I was working on.

  • I opened a file.

  • I copy it and I move it over.

  • It's that easy.

  • I don't even really rely on external libraries to do a lot of this stuff because it's just so simple.

  • But something that isn't simple Estate State is as particularly global state.

  • Um, global state is highly opinionated.

  • There's a new way to manage global state.

  • Every day comes out, there's a new library.

  • It's kind of noisy.

  • Uh, and we're gonna be talking a lot about global state here for the last part of this talk.

  • Global state, in my opinion, is something that's always trying to one up you.

  • Uh, it's because you think you have a handle on it, and then your your requirements change and everything changes in your app, and it kind of pulls the rug out from underneath you.

  • This is usually how global state starts, and you're like, Wow, this is so simple.

  • I have a hook.

  • It's gonna return some global state.

  • Theoretically, and then for some reason, it can quickly turn into something like this.

  • Um, not to name names or anything, but this is something I would try and avoid.

  • I don't I don't wanna have to go to these lengths to use global State.

  • I'm gonna take you on a little bit of a journey of finding global state today, and it's going to be so introspective and enlightening that maybe you'll want to actually handle your own global state.

  • After today, let's start by creating a global store, we can use react context to pass values down our reactor tree, not have to pass them through props.

  • We can start with a store provider.

  • That's just, ah wrapper around context that we'll set up some state with a store with a store and a set store function right there that we can pass down through our context value.

  • And then we'll have a use store custom hook that just wraps around that use context.

  • And what that does is in now, inside of our app, we can declare some initial state past that initial state into our store provider and now that state is going to be available to all the components inside of it, including the little two DUIs component down there and inside of us instead of our components.

  • We can consume the store pretty easily by just calling you store, which gives us the current state of the store and a way to update it, which right now is just set state set states kind of boring, and it's ah prone to break.

  • And I don't really feel good about manipulating the store directly from a component.

  • So what if we took it a step further?

  • To use reducer use reducer is really, um, pretty simple.

  • To use my opinion, just create a reducer function here.

  • We could do it in our app.

  • We can pass that, reduce her function into our store provider we created.

  • And instead of use state now you can just use reduce surpassed the reducer, the initial state and instead of a set state function to directly modify the store.

  • Now we have a dispatcher.

  • We can use sonar components.

  • Instead of importing the set state, we get the dispatch, and now we can dispatch actions on DDE have a little bit more confidence about the actions that are taking place inside of our app, especially for, uh, even four components, like an individual to do where we're not necessarily reading from the store, but just dispatching, weaken, Still import Just, uh, we still important store and get a hold of the dispatcher to send off some events.

  • Um, this pattern is really simple.

  • Will probably get you pretty far, but at the end of the day, it might bring up questions about unnecessary renders.

  • You know, I'm re rendering every component in my app every single time that the state changes.

  • Even if I'm only dispatching well, it turns out we can use a concept called multiple context to aid this a little bit multi context.

  • A multi context global store was first introduced to me by my friend.

  • Can't see Dodds, and he's made it popular through a couple of, um, bog posts, which is just fantastic.

  • And he describes creating two separate context for your store and your dispatcher and sending them both down down the line and creating another use dispatch Custom hook there.

  • So now, inside of our components, if we want the store, we use the use store hook, and if we want the dispatcher we use the use dispatch trick.

  • This makes it so that in components that are only using the dispatcher, they're not going to get updated every single time the store updates.

  • And that will get rid of a lot of weird, unnecessary renders around that scenario.

  • Let's take it a step further.

  • What about a single store?

  • Uh, is probably not going to scale for very long.

  • If you have a lot of global state in your application, you don't want every single component updating when the state that you're consuming updates that global state so we can take it a step further with multiple stores.

  • We can take all of that logic that we had to create our global store and just wrap it into a function.

  • And we can create as many of these global stores as we want by passing them in a producer and initial state.

  • So if we wanted to do store, we just pass it.

  • But to do reducer gives us back the provider and a couple of hooks to consume it, and we're off to the races.

  • We come into our app.

  • We provide our traduced to the entire up and you know what I want some global state to ah, manage.

  • All of my men use being open and stuff like that's all.

  • Make a layout store as well, and I'll wrap that around my app and then inside of our component, like our menu that we wanted to click outside and have it, you know, change around.

  • We can hook it up to that use layout store by both subscribing to the store to see if it's open and the dispatcher to make sure that we can talk about clothes when we click outside of it.

  • The next step in our journey is persistence.

  • Right now, our global store isn't really persisting anything.

  • Um, this is extremely important because your users you're going to reload the page and everything's gonna disappear.

  • So let's throw in some local storage.

  • Local storage is pretty easy use, especially if you're using a reduce.

  • Er so right here we can.

  • Instead, she ate our reducer initial value from local storage, and also, every time I reduce her runs, we can save the resulting state into local storage using a key.

  • And now, every single time that your users reload the page, they're going to get the last state that they were left with.

  • I know this is where you guys want to be.

  • Um, and I'm working hard to get you there just to make sure the everybody is awake like that.

  • I got some T shirts.

  • Oh, all right.

  • Moving on.

  • What about remote data?

  • Persistence.

  • So local storage is great, but eventually users were gonna want some remote data persistence.

  • They're gonna want to store their data on a server.

  • They're gonna want things to sink between their devices.

  • They're so entitled.

  • Um, so let's talk about server ST.

  • You know, I say service state because I believe that it is truly different from global state, but it is the same in spirit.

  • Server ST introduces some interesting challenges around moving from synchronous ap eyes to a synchronous ap eyes.

  • We're no longer interacting with a store or a dispatcher we're interacting with, you know, fetching assets and sending mutations or queries to the server and expecting them to do things right and along Along with that, we're going to notice that a lot of our components where we had previously wired up things like dispatch in the store, that kind of becomes a little bit of a question mark now, where we getting those things?

  • Well, turns out that our components are really great places to compose business logic and user interface together.

  • And this can be so easy that sometimes it's a detriment.

  • We take things like local state mark up you events, styles, things that I feel are meant to be in components.

  • And we kind of mix them together with all of this state and these external utilities or maybe expensive computation or or side effects that are happening on servers, Right?

  • We take all that.

  • We smush it together inside of our components, Um, and sometimes it can make them easy.

  • Oh, hard to reason about.

  • And I'd like to propose a new pattern today where we need to remember that custom hooks are free.

  • Now.

  • There's no cost of adding a new custom hook.

  • Other than cost of abstraction, you can ask, can't see, does what he how he feels about the cost of abstraction.

  • He talks about it a lot, but today I'd like to introduce a new layer of abstraction to our journey.

  • Um, they're just custom hooks.

  • There's really nothing to say other than that, and they're going to proxy all of our business logic between our components and the external service is in the external features that we want to integrate with them.

  • So in this case, we're gonna take our two DUIs component.

  • We're going to write a used to do's custom hook that's going to do all the heavy lifting force between our data.

  • What would that look like?

  • We could just start with a used to do we could import, used to deuce into our component and just expect that it returns to deuce.

  • And that would be enough for now.

  • This used to Deuce Hook could do whatever we wanted to as long as it returned.

  • Seduce.

  • That's all we care about.

  • The implementation details are hidden from the component.

  • It doesn't care that shouldn't.

  • So if we wanted to implement our existing in memory store, we could just import our memories, our store, and return the produce from it, and we would be fine.

  • But remember, we need to talk about a synchronous state and state coming from the server.

  • That changes a lot of things, especially if we're not talking about suspense, which is a whole other talk.

  • But Let's assume that we're not using suspense and this is what something like that would look like.

  • We know that it's gonna be loading.

  • At some point, we know there could be an air or we know we could have two DUIs, and as long as we conform thio this function signature, we can actually do a lot with this custom hook.

  • This used to do's custom hook that we just made.

  • We could use, Ah, an effect and like a tool like Axios to communicate with the rest server that would load our produce for us.

  • And we could manage all of the, you know, the loading state and everything ourselves.

  • Or we could use a very common hook called the use Promise, that will let us pass a function that returns a promise and handle.

  • All of that is loading an error state for us again as long as we're returning.

  • But to do is the loading stating the error.

  • Any component that's consuming two DUIs isn't really going to care.

  • And, you know, we could do the same thing.

  • Take this pattern and apply the same pattern to anything doing a mutation.

  • So if we had you know to do component that is going to toggle something we could have that used toggle to do custom hook, take care of that for us.

  • So instead of importing, you know, some rest a p I or, you know, Axios directly inside of our component, we import use toggle to do we call it and it'll give us a function.

  • You know what?

  • Instead of just giving us function, let's assume it's gonna be a synchronous.

  • It'll give us a function and the loading state and air estate as well.

  • And again that unlocks us to do and locks us the ability to do whatever we want in that use toggle to do hook.

  • We can do whatever we want as long as we're returning a function that that the component can call and it is loading an air state so we could use our in memory store again and just hook it right up to our two.

  • New store is loading.

  • An error is false, and all the time, if we're doing this or we can go into the rest into the rest paradigm and just use Axios too, you know, issue off some type of ah post command to, um, to our server to toggle this to d'oh.

  • But ah, you know, this can get pretty.

  • This can get pretty confusing very quickly if you're if you're managing all these side effects, especially when we start thinking about things like cashing.

  • What happens when we're using these custom hooks multiple times in our app, if we need to do is in multiple places.

  • Are they separate copies?

  • Are we, you know, re duplicating things?

  • Are we creating race conditions?

  • And if we're cashing one of we refreshing that data.

  • You know what?

  • There's so many questions that happened on there.

  • Well, you don't have to stop there.

  • Are used to do's custom hook can, uh, doesn't have to communicate directly with our server.

  • It could use other hooks as well.

  • There's a library created called React Query that helps with scenarios like this.

  • You can use react query to give it a function that returns a promise it'll handle cashing and invalidation and make sure that you're only you know you're not duplicating two DUIs across your entire AP, even if you use it multiple times.

  • I mean, you'll notice that the really cool part is that our return signature is exactly the same.

  • So we just moved from this weird, funky, promise based way of fetching our data to something like react Query.

  • Um, and all of our components that use that used the used to do so didn't have to change it all.

  • It's just an implementation detail.

  • Same thing we could do for the mutations.

  • We could add in react query for the use toggle to d'oh!

  • And in fact, react query handles.

  • Um, React Query handles a lot of the invalidation for mutations, so running this mutation would invalidate all of the other queries on the page getting two DUIs, and it would cause all of them to update.

  • It's not very on similar to tools like Apollo, And I don't use graphic you all whole lot.

  • But when I do, I do enjoy using Apollo and you know, what would it take to switch to something like Apollo?

  • Well, we could import Apollo and make a query used the use query hook from Apollo, and again, we're just returning to do's is loading an error.

  • We just moved from react query to Apollo.

  • We didn't have to change anything in our entire So the same thing with Thea causing a mutation.

  • You know, as long as we're returning that toggle to doing is letting air again.

  • Our mutations don't have to change.

  • So we were able Thio, you know, just in the last five minutes.

  • Wow, go from in memory management of our data to promises to react querian Apollo We could probably even move to relay if we wanted All because we're using this this pattern of creating your own custom hooks to abstract our own business logic in our laps away from our components to make them smarter and to make them more reliable.

  • This pattern to me is extremely powerful.

  • I see basically all the time in my applications.

  • All my components are communicating with custom hooks to handle most of their business logic, which are then reaching out to possibly even more custom hooks or externally, P eyes or whatever.

  • This creates a really nice encapsulation around.

  • You know what is user interface?

  • What is business logic and what are the service is I'm trying to use in my applications.

  • Um, thinking in this thinking from this perspective, you can start to imagine so many great custom hooks that you would want to use inside of your APS.

  • But maybe not something that would be share a bowl outside of your app.

  • You know, it's okay if a custom hook is proprietary to something you're working on, if it makes you more productive, so thes custom hooks.

  • Ah, and the idea of custom hooks is so powerful.

  • I I take things to the extreme, and I create tons of open source libraries built on custom hooks.

  • Those four are just a few of them.

  • React table is, in fact, just a hook.

  • It actually doesn't render anything for you.

  • Um, it just has a hook that gives you everything you need to render your own table.

  • It's a really interesting concept, but ah, my hope and invitation to all of you today is to look at your hooks in your APS and think of ways that you can make yourself more agile abstract to your business logic into your custom hooks and make your components dumber, thereby making them smarter.

  • So, uh, be sure to check me out.

  • Get have twitter YouTube.

  • Come talk to me.

  • I'd love to chat on DA Can't forget nozzle die.

  • Oh, that's my company that I started back in Utah.

  • We build marketing software that helps agencies know where their ranking Google, so thanks.

So, uh, like I said, my name's Tanner Lindsley from Utah.

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

B1 中級

React中的自定義鉤子。最終的UI抽象層--Tanner Linsley | JSConf Hawaii 2020 (Custom Hooks in React: The Ultimate UI Abstraction Layer - Tanner Linsley | JSConf Hawaii 2020)

  • 3 0
    林宜悉 發佈於 2021 年 01 月 14 日
影片單字