The Best Pattern for Conditional Hooks in React

Поделиться
HTML-код
  • Опубликовано: 19 дек 2024

Комментарии • 92

  • @AleksandrTalalaev
    @AleksandrTalalaev Месяц назад +97

    Not bad pattern. But OMG, video is too long for such feature. 2 mins max.

    • @Ahmad_5000
      @Ahmad_5000 Месяц назад +6

      RUclips monetistion rate is increased for videos over 10 mins

    • @mercyiskey9009
      @mercyiskey9009 26 дней назад

      I disagree

    • @witenio
      @witenio 12 дней назад

      @@mercyiskey9009 🤡🤡🤡

    • @jason.zubiate
      @jason.zubiate 3 часа назад

      i agree, took until after the add (5min) to start getting to the point

  • @samisiddiqui5286
    @samisiddiqui5286 Месяц назад +42

    It better to create a child component instead of modifying the hook

    • @babub9617
      @babub9617 Месяц назад +1

      Yep

    • @stanislavzaborkin5603
      @stanislavzaborkin5603 Месяц назад +3

      Thought about the same

    • @deaaaldeen1525
      @deaaaldeen1525 Месяц назад

      I didn't get it

    • @lalasmith2137
      @lalasmith2137 9 часов назад

      can you elaborate please? create a child component and then what?

    • @lalasmith2137
      @lalasmith2137 9 часов назад

      @@babub9617 can you elaborate please? create a child component and then what?

  • @elvinnie1985
    @elvinnie1985 Месяц назад +12

    Cool idea! But my honest opinion is, if you find yourself looking for conditionally rendering hooks, you might have issues in your components composition. Hooks are meant to enrich components with shareable logic. So if you want to conditionally use it, it indicates that you should just split up your components like mentioned in some other comments. Then you only apply the hook to the component that actually needs it.That would be the "clean" way to conditionally render hooks. Make's the API also much cleaner and easier to follow along. Adding these "enabled" arguments makes it very hard to reason about.

    • @mauriciocantu3990
      @mauriciocantu3990 21 день назад +1

      Well, not always true. Imagine a fetch hook that you want to run based on some condition which is something very common. In this case the pattern shown in video comes handy. In fact, this pattern is used by React Query, you can pass a enabled argument to skip/run the hook between rerenders.

  • @mortsdans
    @mortsdans Месяц назад +23

    Can't you just create a child component that calls your hook, and conditionally render the child component if open?

    • @AtizaJuanita
      @AtizaJuanita Месяц назад +1

      That wouldn't suffice all use cases. Some hooks are just plain logic, meaning no JSX involved.

    • @mortsdans
      @mortsdans Месяц назад +5

      @AtizaJuanita right but the hook logic doesn't even run unless the component the hook is in renders

    • @dev_yethiha
      @dev_yethiha Месяц назад +4

      @@mortsdans i agree with u. In most cases when u only need to run logics. Create a child comp instead of hook. return the empty react fragment. write the logic in that comp and conditionally render that Child comp. I believe it will be more feasible in this use case.

    • @PraiseYeezus
      @PraiseYeezus Месяц назад

      hooks are easier to use in various situations than parent/child components. you can use the exact same hook in a drawer component with ease, for example

    • @cosdensolutions
      @cosdensolutions  Месяц назад +5

      you could, but this Dialog is the main component, breaking it up into a child is an extra layer and it's more code/work than simply adding the isEnabled flag. But yes you could do both, this is not an absolute rule and is more useful for understanding how to handle conditional hooks, when you want/need to use them

  • @AviCharlop
    @AviCharlop Месяц назад +13

    if the dialog isn't open then the ref.current is undefined and you already have a return statement that checks for that.

    • @lide24
      @lide24 Месяц назад +2

      The answer should probably be on top as a better solution for this hook

    • @SeanMacLachlan
      @SeanMacLachlan Месяц назад +1

      THIS. This video is just terrible advice.

    • @vadimbich4602
      @vadimbich4602 Месяц назад +3

      yeah, bad example for this "pattern". and some pattern it is, who needs 13 minutes to explain that if you want to skip some code, you need to place early return statement in that code and not in a code which is called from code you want to skip? It is just common sense applied on top of basic understanding of how hooks work.
      Proper pattern would be to refactor the dialog to introduce another component which is mounted conditionally behind isOpen and put the hook call inside that component, problem solved, hooks can't be put in conditional, but components can.

  • @gauravbawa5609
    @gauravbawa5609 Месяц назад +1

    Simple and best solution, thank you so much for making a video on this

  • @_a_9773
    @_a_9773 Месяц назад +13

    It would have been nice to mention the source article u used for the video since u using same example and mostly same explanation

    • @TimaGixe
      @TimaGixe Месяц назад

      what is the article? how to find it?

    • @_a_9773
      @_a_9773 Месяц назад

      Just google "Condition react hook Robin Malfait"

    • @_a_9773
      @_a_9773 Месяц назад +3

      @TimaGixe Conditional react hooks Robin Malfait

    • @cristianecheverria3995
      @cristianecheverria3995 Месяц назад

      @@_a_9773 Thanks for sharing.. Im following Robin now.

    • @TimaGixe
      @TimaGixe Месяц назад

      @@_a_9773 thanks!

  • @Joshuaomorales
    @Joshuaomorales 27 дней назад

    9:40 That function handle on line 15 will still be defined and temporarily stored before the interpreter executes the return statement.

  • @TannerBarcelos
    @TannerBarcelos Месяц назад

    Love this pattern. Gained knowledge about it from using Tanstack Query and experimenting with building my own query hook and attaching the enabled flag. Awesome video, per usual!

  • @georgiydubrov
    @georgiydubrov 23 дня назад

    12:55 explanation about complexity of parameters after cb, but why not just simply pass onClose and not the arrow function? P.s. for those who don't get it - with parentheses

  • @evgenypoboykin7747
    @evgenypoboykin7747 Месяц назад +6

    In this example better solution is to use composition. Create Dialog and Dialog View components. Inside Dialog View use useOutsideClick hook (and encapsulated all logic in here). Inside Dialog return isOpen ? : null.

    • @PraiseYeezus
      @PraiseYeezus Месяц назад +2

      this is an unecessary abstraction. your solution adds an extra component with no real benefit, more layers of logic just makes things harder to reason about.

    • @evgenypoboykin7747
      @evgenypoboykin7747 Месяц назад

      ​ @PraiseYeezus benefit is simple DialogView only render content itself (SRP - single responsibility principle), Dialog render DialogView (or AnyComponent) and mount them in DOM. )))

    • @theReal_WKD
      @theReal_WKD Месяц назад

      That way of programming trying to respect every single "principle" you've ever heard of makes you a bad programmer. The solution shown in the video is the objective best way to do it. The hook itself is responsible for its whole life cycle, no need for an extra component to handle the condionnality just to respect some principle. Your "solution" implies to create a "handler component" everywhere you use the hook.
      You want to use it on , oh now you need a . On , now you need a . This is just dumb and doesn't make the code clearer or more readable. The only benefit is making you feel good for respecting some principle that should be taken with a grain of salt, not applied universally.
      The thing is that you already have a variable for the state of the Dialog, DropDown or whatever (opened or closed). Would it be so bad to pass that state to the hook and handle the logic in there as shown in the video?

  • @mauriciocantu3990
    @mauriciocantu3990 21 день назад

    Nice. This same pattern is used by Tanstack Query to skip/run between rerenders. I've also used it in my projects.

  • @alessandromasiero6611
    @alessandromasiero6611 Месяц назад

    That's so interesting, thank you very much!

  • @akshay__sood
    @akshay__sood Месяц назад +6

    I literally use this same pattern in one of our hooks to conditionally fetch api data. Default is true.

    • @ryanneil4020
      @ryanneil4020 Месяц назад +1

      RTK Query has a similar pattern with the skip object/skipToken

    • @akshay__sood
      @akshay__sood Месяц назад

      @ryanneil4020 we don't use RTK Query in our project so had to write one myself.

  • @Next-Js
    @Next-Js Месяц назад

    I like and subscribe, excellent content, I will watch the videos in order, keep it up!

  • @inakilarramendi787
    @inakilarramendi787 Месяц назад +1

    Won't the main use effect be triggered on each render since you pass a function without calling use call back?

  • @alexeykrupenia3623
    @alexeykrupenia3623 Месяц назад +2

    Hey
    What about arrow function in hook `useOutsideClick` in `Dialog` component?
    Not memoized => a new function is created with every re-render => `useEffect` run's every re-render..

  • @irfansaeedkhan7242
    @irfansaeedkhan7242 Месяц назад

    always useful tips and great content this channel is a gem

  • @harag9
    @harag9 Месяц назад

    Very nice, I too would prefer it at the start of the args, I tend to put my callbacks at the end due to the formatting, though I do tend to pass in a function as a call back so it's just cb={myfunction}

  • @dimitro.cardellini
    @dimitro.cardellini Месяц назад +1

    Oh! I have two concerns. Please forgive me for my bluntness.
    Concern #1: Example
    We have violated the principle of separation of concerns. A single component is responsible both for deciding whether to render and for rendering itself. It seems much simpler to extract isOpen outside of the component and manage it at a higher level, like this: {isOpen && }, rather than updating a hook that is often sourced from external packages (so we need to create another one that wraps original).
    Concern #2: The video title doesn't match its content.
    We still follow the same approach: a fast conditional return from the effect-callback. However, we now need to pass an additional parameter.
    Have a nice coding to all )

  • @YogeshPatil-nw6cn
    @YogeshPatil-nw6cn Месяц назад

    Very good 👍🏼

  • @cristianecheverria3995
    @cristianecheverria3995 Месяц назад

    So the pattern is to pass the conditional to the custom Hook and call the custom hook as we normally do.
    Nice to see you use Vim modes inside VSCode. I recently switched into Neovim and Im loving it. Do you use Vim together with VSCode in your daily job or just is for teaching purposes?

  • @Farruh_13
    @Farruh_13 Месяц назад +3

    I always waste my time wondering how to make my code cleaner, wondering about very little things, eg. in this case i would wonder, should i use enabled flag or instead use just callback as a flag, if there is a callback it's equal to enabled, if !open then i'd pass undefined instead of callback.

  • @joegaffney8006
    @joegaffney8006 Месяц назад

    This falls down when you have hooks that contain other hooks.
    I have faced this problem also and for some expensive hooks used a wrapper component, wasnt to pretty and I do wish there was a built in way to have some conditions to hooks.
    Particularly with using things like react query.
    It’s not so much of a problem on small amounts of components but with large datasets you can really notice even setup time for some hooks gives a performance hit.

  • @rslee
    @rslee Месяц назад

    Am I correct in thinking that once the dialog is opened once, the click event is now registered and your back at square one with the click event in memory even when the dialog is closed? This only helps with performance on first render/mount, right? Would it be beneficial to remove the event listener when enabled becomes false?

    • @dcmbassi
      @dcmbassi Месяц назад

      The return statement in the useEffect cleans up the event listener when the hook unmounts, which happens right before the hook reruns when enabled (i.e. isOpen) becomes false.

    • @rslee
      @rslee Месяц назад

      @dcmbassi oh ok, didn't realize the hook would essentially unmount when rerun but that makes sense. Thanks for the explanation 👌

  • @genostorm7536
    @genostorm7536 Месяц назад

    your videos make me question my react fundamentals lol

  • @taunado
    @taunado Месяц назад

    Apparnetly if you're using a useEffect you've probably messed up your architecture? That's the consenus I've got from industry-working seniors.
    Redux or Tanstack Query generally replaces useEffect, depending on the situation.

    • @cosdensolutions
      @cosdensolutions  Месяц назад +1

      not at all. I use useEffect all of the time. Sure there's an argument for not using useEffect, and I always question if I really need it, but there are many use cases still where it's the only option, like here with event listeners

    • @taunado
      @taunado Месяц назад

      @@cosdensolutions Thanks for clarifying!

    • @theReal_WKD
      @theReal_WKD Месяц назад

      Who are these seniors? People are so afraid of useEffect and it just sounds like a skill issue at some point...

    • @taunado
      @taunado Месяц назад

      @@theReal_WKD People giving talks at conferences for example, blog posts, RUclips videos. There's a lot of controversary around it as it may be handled more gracefully with state management.
      "Cons of useEffect
      Over-reliance for Derived State Logic
      Using useEffect to compute derived state can lead to unnecessary re-renders and complex code. Derived state should instead be computed directly inside the render logic or using useMemo.
      Risk of Dependency Management Issues
      Forgetting to include dependencies or adding the wrong dependencies can cause bugs, infinite loops, or stale data. Managing dependencies correctly can be tricky, especially in complex components.
      Performance Concerns
      If not used carefully, useEffect can introduce performance bottlenecks by triggering unnecessary re-executions or causing heavy operations during re-renders.
      Harder Debugging
      Side effects are harder to test and debug compared to pure functional logic. Issues like race conditions, stale closures, or unintended dependencies can make debugging challenging.
      Overhead for Cleanup
      For effects requiring cleanup (e.g., subscriptions, event listeners), you must implement a cleanup function, adding complexity to the code.
      Concurrency Issues with React 18
      In React 18's concurrent rendering, useEffect may be triggered multiple times due to its nature, which can cause unexpected behavior if effects are not idempotent or properly handled."

  • @RoyalKacperek
    @RoyalKacperek Месяц назад +2

    I think my solution works well, or even better, because the hook is used only when the component needs it. What do u think about it?
    ```
    export default function Dialog({
    isOpen,
    onClose,
    }: {
    isOpen: boolean;
    onClose: () => void;
    }) {
    return isOpen ? : null;
    }
    function DialogContent({ onClose}: { onClose: () => void;}) {
    const elementRef = useRef(null);
    useOutsideClick(isOpen, elementRef, () => {
    onClose();
    });
    return
    }
    ```

    • @_a_9773
      @_a_9773 Месяц назад

      Ur solutiom is mounting and unmounting the component everytime which gonna setup event listners and allocate memory over and over

    • @Jacek2048
      @Jacek2048 Месяц назад +1

      @@_a_9773 There will be no listeners if `isOpen` is false, because the `DialogContent` function will not be called. The `DialogContent` component will only mount/unmount if the value of `isOpen` changes.

    • @_a_9773
      @_a_9773 Месяц назад

      IK I mean everytime u toggling the isOpen variable the Component is mounted/unmounted which means the event lkstners will be added/removed as well as new DOM elements. A less expensive solution is to hide the dialog

    • @RoyalKacperek
      @RoyalKacperek Месяц назад

      ​@@_a_9773 The video shows a simple example. Imagine that you have several different dialogs with more complex functionalities than a dialog that only has a single close function.

    • @McLisak
      @McLisak Месяц назад

      @@_a_9773 It's actually more expensive to keep the DOM node with its listeners all the time. A user might never actually use that dialog. That's why you would actually want to use things only when they are actually needed. The same strategy applies to simple `on{Event}` props. Imagine rendering a component which children has an `onClick` for example. Parent gets unmounted, its children listeners gets cleaned up, then DOM nodes gets removed.

  • @Deus-lo-Vuilt
    @Deus-lo-Vuilt Месяц назад

    Nice!

  • @SathiyaA-e1f
    @SathiyaA-e1f Месяц назад

    Can you make a video for creating product tour in react application ?

  • @wjlee7003
    @wjlee7003 Месяц назад

    If isOpen is already state and changing its value rerenders that dialogue and wherever else it is being used, you dont even need a use effect right? Since isOpen itself is reactive, you can just use vanilla js to conditionally add the click event.

    • @cosdensolutions
      @cosdensolutions  Месяц назад

      you need useEffect to remove the listener when isOpen changes, otherwise you have a memory leak

    • @wjlee7003
      @wjlee7003 Месяц назад

      You can remove event listeners with just JavaScript as well though. You can also use the once option on the event listener which once invoked will be removed. Many ways to clean up without having to use useEffect. I agree it might go away from idiomatic react however.

  • @Aleksey-n5h
    @Aleksey-n5h Месяц назад

    Why couldn't `if(!isOpen) return null` be called before useOutsideClick or before useRef?
    There would be no need to create a useEffect or another useRef

  • @petarkolev6928
    @petarkolev6928 Месяц назад

    Extremely clever solution!

  • @valterszaluzinskis2453
    @valterszaluzinskis2453 21 день назад

    Or also you can put conditional hooks in new conditionaly rendered component

  • @nage7447
    @nage7447 28 дней назад

    well, solution is ok, but you can do better in my opinion, so for fixing react problen just use react, create component DialogOpend with this hook inside and render it conditionally inside Dialog, here we go, issue fixed and no code should rely on some pattern.
    Also I want to hilight a proplem, since you passing callbac function in dependency array useEffect will run every time callback is changed, but it changes on each render sinse you do did not use useCallback hook on it, that means adding and removing listener will be performed on each renderr so it will impact performance.

  • @spacey6960
    @spacey6960 Месяц назад +1

    I have no idea if there ever is a use case for this but what about default react hooks?

    • @_a_9773
      @_a_9773 Месяц назад +1

      What do you mean if there is a use case. He jusg explained the use case

    • @spacey6960
      @spacey6960 Месяц назад

      @_a_9773 i said default hooks. Like useState and useEffect

  • @ekponoambrose1909
    @ekponoambrose1909 Месяц назад +1

    I don't get why all ur videos is always this long, simple concept shouldn't take this long to explain

  • @Rgotto2
    @Rgotto2 Месяц назад

    The best way around this is to leave state in the server and just you JavaScript

  • @ДенисМанченко-к5б
    @ДенисМанченко-к5б Месяц назад

    elementRef in the useEffect deps 💀

  • @floristrading8418
    @floristrading8418 Месяц назад

    I dont need the course, im a senior dev but your videos are so fucking good that im gonna buy it later just to support you

  • @plicha100
    @plicha100 Месяц назад +1

    imo this is the wrong pattern. just move is open outside. this will be most performant code and it will be cleaner as well.

    • @taunado
      @taunado Месяц назад +1

      " you typically don’t need to pass isOpen to the useOnClickOutside hook. Instead, the hook should be responsible only for detecting an outside click and triggering a callback (onClose). This keeps the hook focused and reusable without concern for whether the component is open or closed.
      However, you do pass the elementRef (the reference to the DOM element you want to monitor for outside clicks) and a callback (like onClose) that runs when an outside click is detected. The hook doesn’t need to know or control the open/closed state-it just triggers the callback, and the component manages the state accordingly."

    • @taunado
      @taunado Месяц назад

      "State Management: The Dropdown component manages isOpen, keeping state logic where it belongs and allowing other components to use the hook with their own logic."

  • @matchu-pitchu
    @matchu-pitchu Месяц назад

    Just create a child component and encapsulate the logic. I don't recommend the pattern shown with "enabled". For me it is less readable and maintainable.

  • @ferran5213
    @ferran5213 Месяц назад

    Did bro just make us edge in react? 😶😶
    For an enabled flag?

  • @karlmacz
    @karlmacz Месяц назад

    Good tutorial, but it only took me a few minutes to solve it. using stackoverflow and chatgpt is still faster than playing your video in x2 play speed. just saying